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