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