Upload Tizen:Base source
[framework/base/util-linux-ng.git] / 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
7 #include <unistd.h>
8 #include <errno.h>
9 #include <stdio.h>
10 #include <string.h>
11 #include <sys/stat.h>
12 #include <sys/time.h>
13 #include <time.h>
14 #include <mntent.h>
15 #include "mount_mntent.h"
16 #include "fstab.h"
17 #include "sundries.h"
18 #include "xmalloc.h"
19 #include "fsprobe.h"
20 #include "pathnames.h"
21 #include "nls.h"
22
23 #define streq(s, t)     (strcmp ((s), (t)) == 0)
24
25 /* Information about mtab. ------------------------------------*/
26 static int have_mtab_info = 0;
27 static int var_mtab_does_not_exist = 0;
28 static int var_mtab_is_a_symlink = 0;
29
30 static void
31 get_mtab_info(void) {
32         if (!have_mtab_info) {
33                 struct stat mtab_stat;
34
35                 var_mtab_does_not_exist = 0;
36                 var_mtab_is_a_symlink = 0;
37
38                 if (lstat(_PATH_MOUNTED, &mtab_stat))
39                         var_mtab_does_not_exist = 1;
40                 else if (S_ISLNK(mtab_stat.st_mode))
41                         var_mtab_is_a_symlink = 1;
42                 have_mtab_info = 1;
43         }
44 }
45
46 void
47 reset_mtab_info(void) {
48         have_mtab_info = 0;
49 }
50
51 int
52 mtab_does_not_exist(void) {
53         get_mtab_info();
54         return var_mtab_does_not_exist;
55 }
56
57 static int
58 mtab_is_a_symlink(void) {
59         get_mtab_info();
60         return var_mtab_is_a_symlink;
61 }
62
63 int
64 mtab_is_writable() {
65         int fd;
66
67         /* Should we write to /etc/mtab upon an update?
68            Probably not if it is a symlink to /proc/mounts, since that
69            would create a file /proc/mounts in case the proc filesystem
70            is not mounted. */
71         if (mtab_is_a_symlink())
72                 return 0;
73
74         fd = open(_PATH_MOUNTED, O_RDWR | O_CREAT, 0644);
75         if (fd >= 0) {
76                 close(fd);
77                 return 1;
78         } else
79                 return 0;
80 }
81
82 /* Contents of mtab and fstab ---------------------------------*/
83
84 struct mntentchn mounttable, fstab;
85 static int got_mtab = 0;
86 static int got_fstab = 0;
87
88 static void read_mounttable(void), read_fstab(void);
89
90 struct mntentchn *
91 mtab_head() {
92         if (!got_mtab)
93                 read_mounttable();
94         return &mounttable;
95 }
96
97 struct mntentchn *
98 fstab_head() {
99         if (!got_fstab)
100                 read_fstab();
101         return &fstab;
102 }
103
104 static void
105 my_free_mc(struct mntentchn *mc) {
106         if (mc) {
107                 my_free(mc->m.mnt_fsname);
108                 my_free(mc->m.mnt_dir);
109                 my_free(mc->m.mnt_type);
110                 my_free(mc->m.mnt_opts);
111                 free(mc);
112         }
113 }
114
115
116 static void
117 discard_mntentchn(struct mntentchn *mc0) {
118         struct mntentchn *mc, *mc1;
119
120         for (mc = mc0->nxt; mc && mc != mc0; mc = mc1) {
121                 mc1 = mc->nxt;
122                 my_free_mc(mc);
123         }
124 }
125
126 static void
127 read_mntentchn(mntFILE *mfp, const char *fnam, struct mntentchn *mc0) {
128         struct mntentchn *mc = mc0;
129         struct my_mntent *mnt;
130
131         while ((mnt = my_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                 error(_("warning: error reading %s: %s"),
144                       fnam, strerror (errsv));
145                 mc0->nxt = mc0->prev = NULL;
146         }
147         my_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 = _PATH_MOUNTED;
165         mfp = my_setmntent (fnam, "r");
166         if (mfp == NULL || mfp->mntent_fp == NULL) {
167                 int errsv = errno;
168                 fnam = _PATH_PROC_MOUNTS;
169                 mfp = my_setmntent (fnam, "r");
170                 if (mfp == NULL || mfp->mntent_fp == NULL) {
171                         error(_("warning: can't open %s: %s"),
172                               _PATH_MOUNTED, strerror (errsv));
173                         return;
174                 }
175                 if (verbose)
176                         printf (_("mount: could not open %s - "
177                                   "using %s instead\n"),
178                                 _PATH_MOUNTED, _PATH_PROC_MOUNTS);
179         }
180         read_mntentchn(mfp, fnam, mc);
181 }
182
183 static void
184 read_fstab() {
185         mntFILE *mfp = NULL;
186         const char *fnam;
187         struct mntentchn *mc = &fstab;
188
189         got_fstab = 1;
190         mc->nxt = mc->prev = NULL;
191
192         fnam = _PATH_MNTTAB;
193         mfp = my_setmntent (fnam, "r");
194         if (mfp == NULL || mfp->mntent_fp == NULL) {
195                 int errsv = errno;
196                 error(_("warning: can't open %s: %s"),
197                       _PATH_MNTTAB, strerror (errsv));
198                 return;
199         }
200         read_mntentchn(mfp, fnam, mc);
201 }
202
203
204 /* Given the name NAME, try to find it in mtab.  */
205 struct mntentchn *
206 getmntfile (const char *name) {
207         struct mntentchn *mc, *mc0;
208
209         mc0 = mtab_head();
210         for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
211                 if (streq(mc->m.mnt_dir, name) ||
212                     streq(mc->m.mnt_fsname, name))
213                         return mc;
214         return NULL;
215 }
216
217 /*
218  * Given the directory name NAME, and the place MCPREV we found it last time,
219  * try to find more occurrences.
220  */
221 struct mntentchn *
222 getmntdirbackward (const char *name, struct mntentchn *mcprev) {
223         struct mntentchn *mc, *mc0;
224
225         mc0 = mtab_head();
226         if (!mcprev)
227                 mcprev = mc0;
228         for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev)
229                 if (streq(mc->m.mnt_dir, name))
230                         return mc;
231         return NULL;
232 }
233
234 /*
235  * Given the device name NAME, and the place MCPREV we found it last time,
236  * try to find more occurrences.
237  */
238 struct mntentchn *
239 getmntdevbackward (const char *name, struct mntentchn *mcprev) {
240         struct mntentchn *mc, *mc0;
241
242         mc0 = mtab_head();
243         if (!mcprev)
244                 mcprev = mc0;
245         for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev)
246                 if (streq(mc->m.mnt_fsname, name))
247                         return mc;
248         return NULL;
249 }
250
251 /*
252  * Given the name NAME, check that it occurs precisely once as dir or dev.
253  */
254 int
255 is_mounted_once(const char *name) {
256         struct mntentchn *mc, *mc0;
257         int ct = 0;
258
259         mc0 = mtab_head();
260         for (mc = mc0->prev; mc && mc != mc0; mc = mc->prev)
261                 if (streq(mc->m.mnt_dir, name) ||
262                     streq(mc->m.mnt_fsname, name))
263                         ct++;
264         return (ct == 1);
265 }
266
267 /* Given the name FILE, try to find the option "loop=FILE" in mtab.  */ 
268 struct mntentchn *
269 getmntoptfile (const char *file) {
270         struct mntentchn *mc, *mc0;
271         const char *opts, *s;
272         int l;
273
274         if (!file)
275                 return NULL;
276
277         l = strlen(file);
278
279         mc0 = mtab_head();
280         for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
281                 if ((opts = mc->m.mnt_opts) != NULL
282                     && (s = strstr(opts, "loop="))
283                     && !strncmp(s+5, file, l)
284                     && (s == opts || s[-1] == ',')
285                     && (s[l+5] == 0 || s[l+5] == ','))
286                         return mc;
287         return NULL;
288 }
289
290 /* compares "quoted" or 'quoted' with unquoted */
291 static int
292 streq_quoted(const char *quoted, const char *unquoted)
293 {
294         if (*quoted == '"' || *quoted == '\'')
295                 return !strncmp(quoted + 1, unquoted, strlen(quoted) - 2);
296
297         return streq(quoted, unquoted);
298 }
299
300 static int
301 has_label(const char *device, const char *label) {
302         const char *devlabel;
303         int ret;
304
305         devlabel = fsprobe_get_label_by_devname(device);
306         if (!devlabel)
307                 return 0;
308
309         ret = streq_quoted(label, devlabel);
310         my_free(devlabel);
311         return ret;
312 }
313
314 static int
315 has_uuid(const char *device, const char *uuid){
316         const char *devuuid;
317         int ret;
318
319         devuuid = fsprobe_get_uuid_by_devname(device);
320         if (!devuuid)
321                 return 0;
322
323         ret = streq_quoted(uuid, devuuid);
324         my_free(devuuid);
325         return ret;
326 }
327
328 /* Find the entry (DEV,DIR) in fstab -- spec and dir must be canonicalized! */
329 struct mntentchn *
330 getfs_by_devdir (const char *dev, const char *dir) {
331         struct mntentchn *mc, *mc0;
332
333         mc0 = fstab_head();
334
335         for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) {
336                 int ok = 1;
337
338                 /* dir */
339                 if (!streq(mc->m.mnt_dir, dir)) {
340                         char *dr = canonicalize(mc->m.mnt_dir);
341                         ok = streq(dr, dir);
342                         my_free(dr);
343                 }
344
345                 /* spec */
346                 if (ok && !streq(mc->m.mnt_fsname, dev)) {
347                         const char *fs = mc->m.mnt_fsname;
348
349                         if (strncmp (fs, "LABEL=", 6) == 0) {
350                                 ok = has_label(dev, fs + 6);
351                         } else if (strncmp (fs, "UUID=", 5) == 0) {
352                                 ok = has_uuid(dev, fs + 5);
353                         } else {
354                                 fs = canonicalize_spec(mc->m.mnt_fsname);
355                                 ok = streq(fs, dev);
356                                 my_free(fs);
357                         }
358                 }
359                 if (ok)
360                         return mc;
361         }
362
363         return NULL;
364 }
365
366 /* Find the dir DIR in fstab.  */
367 struct mntentchn *
368 getfs_by_dir (const char *dir) {
369         struct mntentchn *mc, *mc0;
370         char *cdir;
371
372         mc0 = fstab_head();
373         for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
374                 if (streq(mc->m.mnt_dir, dir))
375                         return mc;
376
377         cdir = canonicalize(dir);
378         for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) {
379                 if (streq(mc->m.mnt_dir, cdir)) {
380                         free(cdir);
381                         return mc;
382                 }
383         }
384         free(cdir);
385         return NULL;
386 }
387
388 /* Find the device SPEC in fstab.  */
389 struct mntentchn *
390 getfs_by_spec (const char *spec) {
391         char *name = NULL, *value = NULL, *cspec;
392         struct mntentchn *mc = NULL;
393
394         if (!spec)
395                 return NULL;
396
397         if (fsprobe_parse_spec(spec, &name, &value) != 0)
398                 return NULL;                            /* parse error */
399
400         if (name) {
401                 if (!strcmp(name,"LABEL"))
402                         mc = getfs_by_label (value);
403                 else if (!strcmp(name,"UUID"))
404                         mc = getfs_by_uuid (value);
405
406                 free(name);
407                 free(value);
408                 return mc;
409         }
410
411         cspec = canonicalize_spec(spec);
412         mc = getfs_by_devname(cspec);
413         free(cspec);
414
415         if (!mc)
416                 /* noncanonical name  like /dev/cdrom */
417                 mc = getfs_by_devname(spec);
418
419         return mc;
420 }
421
422 /* Find the device in fstab.  */
423 struct mntentchn *
424 getfs_by_devname (const char *devname) {
425         struct mntentchn *mc, *mc0;
426
427         mc0 = fstab_head();
428
429         /* canonical devname in fstab */
430         for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
431                 if (streq(mc->m.mnt_fsname, devname))
432                         return mc;
433
434         /* noncanonical devname in fstab */
435         for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) {
436                 char *fs;
437
438                 if (strncmp(mc->m.mnt_fsname, "LABEL=", 6) == 0 ||
439                                 strncmp(mc->m.mnt_fsname, "UUID=", 5) == 0)
440                         continue;
441
442                 fs = canonicalize_spec(mc->m.mnt_fsname);
443                 if (streq(fs, devname)) {
444                         free(fs);
445                         return mc;
446                 }
447                 free(fs);
448         }
449
450         return NULL;
451 }
452
453
454 /* Find the uuid UUID in fstab. */
455 struct mntentchn *
456 getfs_by_uuid (const char *uuid) {
457         struct mntentchn *mc, *mc0;
458
459         mc0 = fstab_head();
460         for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
461                 if (strncmp (mc->m.mnt_fsname, "UUID=", 5) == 0
462                     && streq_quoted(mc->m.mnt_fsname + 5, uuid))
463                         return mc;
464         return NULL;
465 }
466
467 /* Find the label LABEL in fstab. */
468 struct mntentchn *
469 getfs_by_label (const char *label) {
470         struct mntentchn *mc, *mc0;
471
472         mc0 = fstab_head();
473         for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
474                 if (strncmp (mc->m.mnt_fsname, "LABEL=", 6) == 0
475                     && streq_quoted(mc->m.mnt_fsname + 6, label))
476                         return mc;
477         return NULL;
478 }
479
480 /* Updating mtab ----------------------------------------------*/
481
482 /* Flag for already existing lock file. */
483 static int we_created_lockfile = 0;
484 static int lockfile_fd = -1;
485
486 /* Flag to indicate that signals have been set up. */
487 static int signals_have_been_setup = 0;
488
489 /* Ensure that the lock is released if we are interrupted.  */
490 extern char *strsignal(int sig);        /* not always in <string.h> */
491
492 static void
493 handler (int sig) {
494      die(EX_USER, "%s", strsignal(sig));
495 }
496
497 static void
498 setlkw_timeout (int sig) {
499      /* nothing, fcntl will fail anyway */
500 }
501
502 /* Remove lock file.  */
503 void
504 unlock_mtab (void) {
505         if (we_created_lockfile) {
506                 close(lockfile_fd);
507                 lockfile_fd = -1;
508                 unlink (_PATH_MOUNTED_LOCK);
509                 we_created_lockfile = 0;
510         }
511 }
512
513 /* Create the lock file.
514    The lock file will be removed if we catch a signal or when we exit. */
515 /* The old code here used flock on a lock file /etc/mtab~ and deleted
516    this lock file afterwards. However, as rgooch remarks, that has a
517    race: a second mount may be waiting on the lock and proceed as
518    soon as the lock file is deleted by the first mount, and immediately
519    afterwards a third mount comes, creates a new /etc/mtab~, applies
520    flock to that, and also proceeds, so that the second and third mount
521    now both are scribbling in /etc/mtab.
522    The new code uses a link() instead of a creat(), where we proceed
523    only if it was us that created the lock, and hence we always have
524    to delete the lock afterwards. Now the use of flock() is in principle
525    superfluous, but avoids an arbitrary sleep(). */
526
527 /* Where does the link point to? Obvious choices are mtab and mtab~~.
528    HJLu points out that the latter leads to races. Right now we use
529    mtab~.<pid> instead. Use 20 as upper bound for the length of %d. */
530 #define MOUNTLOCK_LINKTARGET            _PATH_MOUNTED_LOCK "%d"
531 #define MOUNTLOCK_LINKTARGET_LTH        (sizeof(_PATH_MOUNTED_LOCK)+20)
532
533 /*
534  * The original mount locking code has used sleep(1) between attempts and
535  * maximal number of attemps has been 5.
536  *
537  * There was very small number of attempts and extremely long waiting (1s)
538  * that is useless on machines with large number of concurret mount processes.
539  *
540  * Now we wait few thousand microseconds between attempts and we have global
541  * time limit (30s) rather than limit for number of attempts. The advantage
542  * is that this method also counts time which we spend in fcntl(F_SETLKW) and
543  * number of attempts is not so much restricted.
544  *
545  * -- kzak@redhat.com [2007-Mar-2007]
546  */
547
548 /* maximum seconds between first and last attempt */
549 #define MOUNTLOCK_MAXTIME               30
550
551 /* sleep time (in microseconds, max=999999) between attempts */
552 #define MOUNTLOCK_WAITTIME              5000
553
554 void
555 lock_mtab (void) {
556         int i;
557         struct timespec waittime;
558         struct timeval maxtime;
559         char linktargetfile[MOUNTLOCK_LINKTARGET_LTH];
560
561         if (!signals_have_been_setup) {
562                 int sig = 0;
563                 struct sigaction sa;
564
565                 sa.sa_handler = handler;
566                 sa.sa_flags = 0;
567                 sigfillset (&sa.sa_mask);
568
569                 while (sigismember (&sa.sa_mask, ++sig) != -1
570                        && sig != SIGCHLD) {
571                         if (sig == SIGALRM)
572                                 sa.sa_handler = setlkw_timeout;
573                         else
574                                 sa.sa_handler = handler;
575                         sigaction (sig, &sa, (struct sigaction *) 0);
576                 }
577                 signals_have_been_setup = 1;
578         }
579
580         sprintf(linktargetfile, MOUNTLOCK_LINKTARGET, getpid ());
581
582         i = open (linktargetfile, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
583         if (i < 0) {
584                 int errsv = errno;
585                 /* linktargetfile does not exist (as a file)
586                    and we cannot create it. Read-only filesystem?
587                    Too many files open in the system?
588                    Filesystem full? */
589                 die (EX_FILEIO, _("can't create lock file %s: %s "
590                                                   "(use -n flag to override)"),
591                          linktargetfile, strerror (errsv));
592         }
593         close(i);
594
595         gettimeofday(&maxtime, NULL);
596         maxtime.tv_sec += MOUNTLOCK_MAXTIME;
597
598         waittime.tv_sec = 0;
599         waittime.tv_nsec = (1000 * MOUNTLOCK_WAITTIME);
600
601         /* Repeat until it was us who made the link */
602         while (!we_created_lockfile) {
603                 struct timeval now;
604                 struct flock flock;
605                 int errsv, j;
606
607                 j = link(linktargetfile, _PATH_MOUNTED_LOCK);
608                 errsv = errno;
609
610                 if (j == 0)
611                         we_created_lockfile = 1;
612
613                 if (j < 0 && errsv != EEXIST) {
614                         (void) unlink(linktargetfile);
615                         die (EX_FILEIO, _("can't link lock file %s: %s "
616                              "(use -n flag to override)"),
617                              _PATH_MOUNTED_LOCK, strerror (errsv));
618                 }
619
620                 lockfile_fd = open (_PATH_MOUNTED_LOCK, O_WRONLY);
621
622                 if (lockfile_fd < 0) {
623                         /* Strange... Maybe the file was just deleted? */
624                         int errsv = errno;
625                         gettimeofday(&now, NULL);
626                         if (errno == ENOENT && now.tv_sec < maxtime.tv_sec) {
627                                 we_created_lockfile = 0;
628                                 continue;
629                         }
630                         (void) unlink(linktargetfile);
631                         die (EX_FILEIO, _("can't open lock file %s: %s "
632                              "(use -n flag to override)"),
633                              _PATH_MOUNTED_LOCK, strerror (errsv));
634                 }
635
636                 flock.l_type = F_WRLCK;
637                 flock.l_whence = SEEK_SET;
638                 flock.l_start = 0;
639                 flock.l_len = 0;
640
641                 if (j == 0) {
642                         /* We made the link. Now claim the lock. */
643                         if (fcntl (lockfile_fd, F_SETLK, &flock) == -1) {
644                                 if (verbose) {
645                                     int errsv = errno;
646                                     printf(_("Can't lock lock file %s: %s\n"),
647                                            _PATH_MOUNTED_LOCK, strerror (errsv));
648                                 }
649                                 /* proceed, since it was us who created the lockfile anyway */
650                         }
651                         (void) unlink(linktargetfile);
652                 } else {
653                         /* Someone else made the link. Wait. */
654                         gettimeofday(&now, NULL);
655                         if (now.tv_sec < maxtime.tv_sec) {
656                                 alarm(maxtime.tv_sec - now.tv_sec);
657                                 if (fcntl (lockfile_fd, F_SETLKW, &flock) == -1) {
658                                         int errsv = errno;
659                                         (void) unlink(linktargetfile);
660                                         die (EX_FILEIO, _("can't lock lock file %s: %s"),
661                                              _PATH_MOUNTED_LOCK, (errno == EINTR) ?
662                                              _("timed out") : strerror (errsv));
663                                 }
664                                 alarm(0);
665
666                                 nanosleep(&waittime, NULL);
667                         } else {
668                                 (void) unlink(linktargetfile);
669                                 die (EX_FILEIO, _("Cannot create link %s\n"
670                                                   "Perhaps there is a stale lock file?\n"),
671                                          _PATH_MOUNTED_LOCK);
672                         }
673                         close(lockfile_fd);
674                 }
675         }
676 }
677
678 static char *
679 get_option(const char *optname, const char *src, size_t *len)
680 {
681         char *opt, *end;
682         size_t sz;
683
684         if (!src)
685                 return NULL;
686
687         opt = strstr(src, optname);
688         if (!opt)
689                 return NULL;
690
691         end = strchr(opt, ',');
692         sz = end ? end - opt : strlen(opt);
693
694         if (len)
695                 *len = sz;
696
697         if ((opt == src || *(opt - 1) == ',') &&
698             (*(opt + sz) == '\0' || *(opt + sz) == ','))
699                 return opt;
700
701         return NULL;
702 }
703
704 static int
705 cpy_option(const char *optname, char *dest, const char *src)
706 {
707         char *opt;
708         size_t sz;
709
710         opt = get_option(optname, src, &sz);
711         if (!opt)
712                 /* the option doesn't exist */
713                 return 0;
714
715         if (get_option(optname, dest, NULL))
716                 /* the options is already in dest */
717                 return 0;
718
719         if (*dest) {
720                 dest = dest + strlen(dest);
721                 *dest++ = ',';          /* separator */
722         }
723         memcpy(dest, opt, sz);  /* copy option */
724         *(dest + sz) = '\0';    /* terminator */
725
726         return 1;
727 }
728
729 /* Generates (and allocates) new options for remount
730  *
731  * We cannot blindly replace the old options, otherwise we will lost some
732  * internally generated stuff (e.g loop=).
733  */
734 static char *
735 mk_remount_opts(const char *old, const char *instead)
736 {
737         char *new;
738         size_t sz;
739
740         if (old == NULL && instead == NULL)
741                 return NULL;
742         if (!old)
743                 return xstrdup(instead);
744
745         /* max size of new options is:
746          * old + new + '\0' + separator (for each copied option)
747          */
748         sz = strlen(old) + (instead ? strlen(instead) : 0) + 2;
749         new = xmalloc(sz);
750         if (instead && *instead)
751                 strncpy(new, instead, sz);
752         else
753                 *new = '\0';
754
755         cpy_option("loop=", new, old);
756
757         return new;
758 }
759
760 /*
761  * Update the mtab.
762  *  Used by umount with null INSTEAD: remove the last DIR entry.
763  *  Used by mount upon a remount: update option part,
764  *   and complain if a wrong device or type was given.
765  *   [Note that often a remount will be a rw remount of /
766  *    where there was no entry before, and we'll have to believe
767  *    the values given in INSTEAD.]
768  */
769
770 void
771 update_mtab (const char *dir, struct my_mntent *instead) {
772         mntFILE *mfp, *mftmp;
773         const char *fnam = _PATH_MOUNTED;
774         struct mntentchn mtabhead;      /* dummy */
775         struct mntentchn *mc, *mc0, *absent = NULL;
776         struct stat sbuf;
777         int fd;
778
779         if (mtab_does_not_exist() || !mtab_is_writable())
780                 return;
781
782         lock_mtab();
783
784         /* having locked mtab, read it again */
785         mc0 = mc = &mtabhead;
786         mc->nxt = mc->prev = NULL;
787
788         mfp = my_setmntent(fnam, "r");
789         if (mfp == NULL || mfp->mntent_fp == NULL) {
790                 int errsv = errno;
791                 error (_("cannot open %s (%s) - mtab not updated"),
792                        fnam, strerror (errsv));
793                 goto leave;
794         }
795
796         read_mntentchn(mfp, fnam, mc);
797
798         /* find last occurrence of dir */
799         for (mc = mc0->prev; mc && mc != mc0; mc = mc->prev)
800                 if (streq(mc->m.mnt_dir, dir))
801                         break;
802         if (mc && mc != mc0) {
803                 if (instead == NULL) {
804                         /* An umount - remove entry */
805                         if (mc && mc != mc0) {
806                                 mc->prev->nxt = mc->nxt;
807                                 mc->nxt->prev = mc->prev;
808                                 my_free_mc(mc);
809                         }
810                 } else if (!strcmp(mc->m.mnt_dir, instead->mnt_dir)) {
811                         /* A remount */
812                         char *opts = mk_remount_opts(mc->m.mnt_opts,
813                                         instead->mnt_opts);
814                         my_free(mc->m.mnt_opts);
815                         mc->m.mnt_opts = opts;
816                 } else {
817                         /* A move */
818                         my_free(mc->m.mnt_dir);
819                         mc->m.mnt_dir = xstrdup(instead->mnt_dir);
820                 }
821         } else if (instead) {
822                 /* not found, add a new entry */
823                 absent = xmalloc(sizeof(*absent));
824                 absent->m.mnt_fsname = xstrdup(instead->mnt_fsname);
825                 absent->m.mnt_dir = xstrdup(instead->mnt_dir);
826                 absent->m.mnt_type = xstrdup(instead->mnt_type);
827                 absent->m.mnt_opts = xstrdup(instead->mnt_opts);
828                 absent->m.mnt_freq = instead->mnt_freq;
829                 absent->m.mnt_passno = instead->mnt_passno;
830                 absent->nxt = mc0;
831                 if (mc0->prev != NULL) {
832                         absent->prev = mc0->prev;
833                         mc0->prev->nxt = absent;
834                 } else {
835                         absent->prev = mc0;
836                 }
837                 mc0->prev = absent;
838                 if (mc0->nxt == NULL)
839                         mc0->nxt = absent;
840         }
841
842         /* write chain to mtemp */
843         mftmp = my_setmntent (_PATH_MOUNTED_TMP, "w");
844         if (mftmp == NULL || mftmp->mntent_fp == NULL) {
845                 int errsv = errno;
846                 error (_("cannot open %s (%s) - mtab not updated"),
847                        _PATH_MOUNTED_TMP, strerror (errsv));
848                 discard_mntentchn(mc0);
849                 goto leave;
850         }
851
852         for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) {
853                 if (my_addmntent(mftmp, &(mc->m)) == 1) {
854                         int errsv = errno;
855                         die (EX_FILEIO, _("error writing %s: %s"),
856                              _PATH_MOUNTED_TMP, strerror (errsv));
857                 }
858         }
859
860         discard_mntentchn(mc0);
861         fd = fileno(mftmp->mntent_fp);
862
863         /*
864          * It seems that better is incomplete and broken /mnt/mtab that
865          * /mnt/mtab that is writeable for non-root users.
866          *
867          * We always skip rename() when chown() and chmod() failed.
868          * -- kzak, 11-Oct-2007
869          */
870
871         if (fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
872                 int errsv = errno;
873                 fprintf(stderr, _("error changing mode of %s: %s\n"),
874                         _PATH_MOUNTED_TMP, strerror (errsv));
875                 goto leave;
876         }
877
878         /*
879          * If mount is setuid and some non-root user mounts sth,
880          * then mtab.tmp might get the group of this user. Copy uid/gid
881          * from the present mtab before renaming.
882          */
883         if (stat(_PATH_MOUNTED, &sbuf) == 0) {
884                 if (fchown(fd, sbuf.st_uid, sbuf.st_gid) < 0) {
885                         int errsv = errno;
886                         fprintf (stderr, _("error changing owner of %s: %s\n"),
887                                 _PATH_MOUNTED_TMP, strerror(errsv));
888                         goto leave;
889                 }
890         }
891
892         my_endmntent (mftmp);
893
894         /* rename mtemp to mtab */
895         if (rename (_PATH_MOUNTED_TMP, _PATH_MOUNTED) < 0) {
896                 int errsv = errno;
897                 fprintf(stderr, _("can't rename %s to %s: %s\n"),
898                         _PATH_MOUNTED_TMP, _PATH_MOUNTED, strerror(errsv));
899         }
900
901  leave:
902         unlock_mtab();
903 }
904
905
906 #ifdef MAIN_TEST_MTABLOCK
907
908 /*
909  * This is mtab locking code test for:
910  *
911  *      - performance (how many concurrent processes)
912  *
913  *      - lock reliability (is possible to see corrupted data  if more
914  *                          concurrent processes modify a same file)
915  *
916  *  The test is very simple -- it reads a number from locked file, increments the
917  *  number and writes the number back to the file.
918  */
919 /* dummy */
920 char *fsprobe_get_label_by_devname(const char *spec) { return NULL; }
921 char *fsprobe_get_uuid_by_devname(const char *spec) { return NULL; }
922 int fsprobe_parse_spec(const char *spec, char **name, char **value) { return 0; }
923 struct my_mntent *my_getmntent (mntFILE *mfp) { return NULL; }
924 mntFILE *my_setmntent (const char *file, char *mode) { return NULL; }
925 void my_endmntent (mntFILE *mfp) { }
926 int my_addmntent (mntFILE *mfp, struct my_mntent *mnt) { return 0; }
927
928 int
929 main(int argc, char **argv)
930 {
931         time_t synctime;
932         char *filename;
933         int nloops, id, i;
934         pid_t pid = getpid();
935         unsigned int usecs;
936         struct timeval tv;
937         struct stat st;
938         long last = 0;
939
940         progname = argv[0];
941
942         if (argc < 3)
943                 die(EXIT_FAILURE,
944                         "usage: %s <id> <synctime> <file> <nloops>\n",
945                         progname);
946
947         id = atoi(argv[1]);
948         synctime = (time_t) atol(argv[2]);
949         filename = argv[3];
950         nloops = atoi(argv[4]);
951
952         if (stat(filename, &st) < -1)
953                 die(EXIT_FAILURE, "%s: %s\n", filename, strerror(errno));
954
955         fprintf(stderr, "%05d (pid=%05d): START\n", id, pid);
956
957         gettimeofday(&tv, NULL);
958         if (synctime && synctime - tv.tv_sec > 1) {
959                 usecs = ((synctime - tv.tv_sec) * 1000000UL) -
960                                         (1000000UL - tv.tv_usec);
961                 usleep(usecs);
962         }
963
964         for (i = 0; i < nloops; i++) {
965                 FILE *f;
966                 long num;
967                 char buf[256];
968
969                 lock_mtab();
970
971                 if (!(f = fopen(filename, "r"))) {
972                         unlock_mtab();
973                         die(EXIT_FAILURE, "ERROR: %d (pid=%d, loop=%d): "
974                                         "open for read failed\n", id, pid, i);
975                 }
976                 if (!fgets(buf, sizeof(buf), f)) {
977                         unlock_mtab();
978                         die(EXIT_FAILURE, "ERROR: %d (pid=%d, loop=%d): "
979                                         "read failed\n", id, pid, i);
980                 }
981                 fclose(f);
982
983                 num = atol(buf) + 1;
984
985                 if (!(f = fopen(filename, "w"))) {
986                         unlock_mtab();
987                         die(EXIT_FAILURE, "ERROR: %d (pid=%d, loop=%d): "
988                                         "open for write failed\n", id, pid, i);
989                 }
990                 fprintf(f, "%ld", num);
991                 fclose(f);
992
993                 unlock_mtab();
994
995                 gettimeofday(&tv, NULL);
996
997                 fprintf(stderr, "%010ld.%06ld %04d (pid=%05d, loop=%05d): "
998                                 "num=%09ld last=%09ld\n",
999                                 tv.tv_sec, tv.tv_usec, id,
1000                                 pid, i, num, last);
1001                 last = num;
1002
1003                 /* The mount command usually finish after mtab update. We
1004                  * simulate this via short sleep -- it's also enough to make
1005                  * concurrent processes happy.
1006                  */
1007                 usleep(50000);
1008         }
1009
1010         fprintf(stderr, "%05d (pid=%05d): DONE\n", id, pid);
1011
1012         exit(EXIT_SUCCESS);
1013 }
1014 #endif
1015