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