]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/mount/fstab.c
a742e648d0e4d133c7e24eb07f2c4d591ad44cd5
[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_handler = handler;
335                 sa.sa_flags = 0;
336                 sigfillset (&sa.sa_mask);
337   
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);
345                 }
346                 signals_have_been_setup = 1;
347         }
348
349         sprintf(linktargetfile, MOUNTLOCK_LINKTARGET, getpid ());
350
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));
361         }
362         close(i);
363         
364         /* Repeat until it was us who made the link */
365         while (!we_created_lockfile) {
366                 struct flock flock;
367                 int j;
368
369                 j = link(linktargetfile, MOUNTED_LOCK);
370
371                 {
372                         int errsv = errno;
373
374                         if (j == 0)
375                                 we_created_lockfile = 1;
376
377                         if (j < 0 && errsv != EEXIST) {
378                                 (void) unlink(linktargetfile);
379                                 die (EX_FILEIO, _("can't link lock file %s: %s "
380                                      "(use -n flag to override)"),
381                                      MOUNTED_LOCK, strerror (errsv));
382                         }
383                 }
384
385                 lockfile_fd = open (MOUNTED_LOCK, O_WRONLY);
386
387                 if (lockfile_fd < 0) {
388                         int errsv = errno;
389                         /* Strange... Maybe the file was just deleted? */
390                         if (errno == ENOENT && tries-- > 0) {
391                                 if (tries % 200 == 0)
392                                         usleep(30);
393                                 continue;
394                         }
395                         (void) unlink(linktargetfile);
396                         die (EX_FILEIO, _("can't open lock file %s: %s "
397                              "(use -n flag to override)"),
398                              MOUNTED_LOCK, strerror (errsv));
399                 }
400
401                 flock.l_type = F_WRLCK;
402                 flock.l_whence = SEEK_SET;
403                 flock.l_start = 0;
404                 flock.l_len = 0;
405
406                 if (j == 0) {
407                         /* We made the link. Now claim the lock. */
408                         if (fcntl (lockfile_fd, F_SETLK, &flock) == -1) {
409                                 if (verbose) {
410                                     int errsv = errno;
411                                     nfs_error(_("%s: Can't lock lock file "
412                                                 "%s: %s"), progname,
413                                                 MOUNTED_LOCK,
414                                                 strerror (errsv));
415                                 }
416                                 /* proceed anyway */
417                         }
418                         (void) unlink(linktargetfile);
419                 } else {
420                         static int retries = 0;
421
422                         /* Someone else made the link. Wait. */
423                         alarm(LOCK_TIMEOUT);
424                         if (fcntl (lockfile_fd, F_SETLKW, &flock) == -1) {
425                                 int errsv = errno;
426                                 (void) unlink(linktargetfile);
427                                 die (EX_FILEIO, _("can't lock lock file %s: %s"),
428                                      MOUNTED_LOCK, (errno == EINTR) ?
429                                      _("timed out") : strerror (errsv));
430                         }
431                         alarm(0);
432                         /* Limit the number of iterations - maybe there
433                            still is some old /etc/mtab~ */
434                         ++retries;
435                         if (retries % 200 == 0)
436                            usleep(30);
437                         if (retries > 100000) {
438                                 (void) unlink(linktargetfile);
439                                 close(lockfile_fd);
440                                 die (EX_FILEIO, _("Cannot create link %s\n"
441                                                   "Perhaps there is a stale lock file?\n"),
442                                          MOUNTED_LOCK);
443                         }
444                         close(lockfile_fd);
445                 }
446         }
447 }
448
449 /*
450  * Update the mtab.
451  *  Used by umount with null INSTEAD: remove the last DIR entry.
452  *  Used by mount upon a remount: update option part,
453  *   and complain if a wrong device or type was given.
454  *   [Note that often a remount will be a rw remount of /
455  *    where there was no entry before, and we'll have to believe
456  *    the values given in INSTEAD.]
457  */
458
459 void
460 update_mtab (const char *dir, struct mntent *instead)
461 {
462         mntFILE *mfp, *mftmp;
463         const char *fnam = MOUNTED;
464         struct mntentchn mtabhead;      /* dummy */
465         struct mntentchn *mc, *mc0, *absent = NULL;
466
467         if (mtab_does_not_exist() || !mtab_is_writable())
468                 return;
469
470         lock_mtab();
471
472         /* having locked mtab, read it again */
473         mc0 = mc = &mtabhead;
474         mc->nxt = mc->prev = NULL;
475
476         mfp = nfs_setmntent(fnam, "r");
477         if (mfp == NULL || mfp->mntent_fp == NULL) {
478                 int errsv = errno;
479                 nfs_error (_("cannot open %s (%s) - mtab not updated"),
480                        fnam, strerror (errsv));
481                 goto leave;
482         }
483
484         read_mntentchn(mfp, fnam, mc);
485
486         /* find last occurrence of dir */
487         for (mc = mc0->prev; mc && mc != mc0; mc = mc->prev)
488                 if (streq(mc->m.mnt_dir, dir))
489                         break;
490         if (mc && mc != mc0) {
491                 if (instead == NULL) {
492                         /* An umount - remove entry */
493                         if (mc && mc != mc0) {
494                                 mc->prev->nxt = mc->nxt;
495                                 mc->nxt->prev = mc->prev;
496                                 free(mc);
497                         }
498                 } else {
499                         /* A remount */
500                         mc->m.mnt_opts = instead->mnt_opts;
501                 }
502         } else if (instead) {
503                 /* not found, add a new entry */
504                 absent = xmalloc(sizeof(*absent));
505                 absent->m = *instead;
506                 absent->nxt = mc0;
507                 absent->prev = mc0->prev;
508                 mc0->prev = absent;
509                 if (mc0->nxt == NULL)
510                         mc0->nxt = absent;
511         }
512
513         /* write chain to mtemp */
514         mftmp = nfs_setmntent (MOUNTED_TEMP, "w");
515         if (mftmp == NULL || mftmp->mntent_fp == NULL) {
516                 int errsv = errno;
517                 nfs_error (_("cannot open %s (%s) - mtab not updated"),
518                        MOUNTED_TEMP, strerror (errsv));
519                 goto leave;
520         }
521
522         for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) {
523                 if (nfs_addmntent(mftmp, &(mc->m)) == 1) {
524                         int errsv = errno;
525                         die (EX_FILEIO, _("error writing %s: %s"),
526                              MOUNTED_TEMP, strerror (errsv));
527                 }
528         }
529
530 #if 0
531         /* the chain might have strings copied from 'instead',
532          * so we cannot safely free it.
533          * And there is no need anyway because we are going to exit
534          * shortly.  So just don't call discard_mntentchn....
535          */
536         discard_mntentchn(mc0);
537 #endif
538         if (fchmod (fileno (mftmp->mntent_fp),
539                     S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
540                 int errsv = errno;
541                 nfs_error(_("%s: error changing mode of %s: %s"),
542                                 progname, MOUNTED_TEMP, strerror (errsv));
543         }
544         nfs_endmntent (mftmp);
545
546         { /*
547            * If mount is setuid and some non-root user mounts sth,
548            * then mtab.tmp might get the group of this user. Copy uid/gid
549            * from the present mtab before renaming.
550            */
551             struct stat sbuf;
552             if (stat (MOUNTED, &sbuf) == 0) {
553                         if (chown (MOUNTED_TEMP, sbuf.st_uid, sbuf.st_gid) < 0) {
554                                 nfs_error(_("%s: error changing owner of %s: %s"),
555                                         progname, MOUNTED_TEMP, strerror (errno));
556                         }
557                 }
558         }
559
560         /* rename mtemp to mtab */
561         if (rename (MOUNTED_TEMP, MOUNTED) < 0) {
562                 int errsv = errno;
563                 nfs_error(_("%s: can't rename %s to %s: %s\n"),
564                                 progname, MOUNTED_TEMP, MOUNTED,
565                                 strerror(errsv));
566         }
567
568  leave:
569         unlock_mtab();
570 }