Gentoo Websites Logo
Go to: Gentoo Home Documentation Forums Lists Bugs Planet Store Wiki Get Gentoo!
View | Details | Raw Unified | Return to bug 98403 | Differences between
and this patch

Collapse All | Expand All

(-)util-linux-ng-2.13.1.1/mount/fstab.c (-69 / +117 lines)
Lines 2-7 Link Here
2
 * - added Native Language Support
2
 * - added Native Language Support
3
 * Sun Mar 21 1999 - Arnaldo Carvalho de Melo <acme@conectiva.com.br>
3
 * Sun Mar 21 1999 - Arnaldo Carvalho de Melo <acme@conectiva.com.br>
4
 * - fixed strerr(errno) in gettext calls
4
 * - fixed strerr(errno) in gettext calls
5
 * 2003-08-08 Thomas Hood <jdthood@yahoo.co.uk> with help from Patrick McLean
6
 * - Write through a symlink at /etc/mtab if it doesn't point into /proc/
5
 */
7
 */
6
8
7
#include <unistd.h>
9
#include <unistd.h>
Lines 13-71 Link Here
13
#include <time.h>
15
#include <time.h>
14
#include "mount_mntent.h"
16
#include "mount_mntent.h"
15
#include "fstab.h"
17
#include "fstab.h"
18
#include "realpath.h"
16
#include "sundries.h"
19
#include "sundries.h"
17
#include "xmalloc.h"
20
#include "xmalloc.h"
18
#include "fsprobe.h"
21
#include "fsprobe.h"
19
#include "mount_paths.h"
22
#include "mount_paths.h"
20
#include "nls.h"
23
#include "nls.h"
21
24
22
#define streq(s, t)	(strcmp ((s), (t)) == 0)
25
/* A 64 bit number can be displayed in 20 decimal digits */
23
26
#define LEN_LARGEST_PID 20
24
#define PROC_MOUNTS		"/proc/mounts"
27
#define MTAB_PATH_MAX (PATH_MAX - (sizeof(MTAB_LOCK_SUFFIX) - 1) - LEN_LARGEST_PID)
25
26
28
27
/* Information about mtab. ------------------------------------*/
29
/* Information about mtab. ------------------------------------*/
28
static int have_mtab_info = 0;
30
static char mtab_path[MTAB_PATH_MAX];
29
static int var_mtab_does_not_exist = 0;
31
static char mtab_lock_path[PATH_MAX];
30
static int var_mtab_is_a_symlink = 0;
32
static char mtab_lock_targ[PATH_MAX];
33
static char mtab_temp_path[PATH_MAX];
31
34
32
static void
35
/*
36
 * Set mtab_path to the real path of the mtab file
37
 * or to the null string if that path is inaccessible
38
 *
39
 * Run this early
40
 */
41
void
33
get_mtab_info(void) {
42
get_mtab_info(void) {
34
	struct stat mtab_stat;
43
	struct stat mtab_stat;
35
44
36
	if (!have_mtab_info) {
45
	if (lstat(MOUNTED, &mtab_stat)) {
37
		if (lstat(MOUNTED, &mtab_stat))
46
		/* Assume that the lstat error means that the file does not exist */
38
			var_mtab_does_not_exist = 1;
47
		/* (Maybe we should check errno here) */
39
		else if (S_ISLNK(mtab_stat.st_mode))
48
		strcpy(mtab_path, MOUNTED);
40
			var_mtab_is_a_symlink = 1;
49
	} else if (S_ISLNK(mtab_stat.st_mode)) {
41
		have_mtab_info = 1;
50
		/* Is a symlink */
51
		int len;
52
		char *r = myrealpath(MOUNTED, mtab_path, MTAB_PATH_MAX);
53
		mtab_path[MTAB_PATH_MAX - 1] = 0; /* Just to be sure */
54
		len = strlen(mtab_path);
55
		if (
56
			r == NULL
57
			|| len == 0
58
			|| len >= (MTAB_PATH_MAX - 1)
59
			|| streqn(mtab_path, PATH_PROC, sizeof(PATH_PROC) - 1)
60
		) {
61
			/* Real path invalid or inaccessible */
62
			mtab_path[0] = '\0';
63
			return;
64
		}
65
		/* mtab_path now contains mtab's real path */
66
	} else {
67
		/* Exists and is not a symlink */
68
		strcpy(mtab_path, MOUNTED);
42
	}
69
	}
70
71
	sprintf(mtab_lock_path, "%s%s", mtab_path, MTAB_LOCK_SUFFIX);
72
	sprintf(mtab_lock_targ, "%s%s%d", mtab_path, MTAB_LOCK_SUFFIX, getpid());
73
	sprintf(mtab_temp_path, "%s%s", mtab_path, MTAB_TEMP_SUFFIX);
43
}
74
}
44
75
76
77
/*
78
 * Tell whether or not the mtab real path is accessible
79
 *
80
 * get_mtab_info() must have been run
81
 */
82
static int
83
mtab_is_accessible(void) {
84
	return (mtab_path[0] != '\0');
85
}
86
 
87
/*
88
 * Tell whether or not the mtab file currently exists
89
 *
90
 * Note that the answer here is independent of whether or
91
 * not the file is writable, so if you are planning to create
92
 * the mtab file then check mtab_is_writable() too.
93
 *
94
 * get_mtab_info() must have been run
95
 */
45
int
96
int
46
mtab_does_not_exist(void) {
97
mtab_does_not_exist(void) {
47
	get_mtab_info();
98
	struct stat mtab_stat;
48
	return var_mtab_does_not_exist;
49
}
50
99
51
static int
100
	if (!mtab_is_accessible())
52
mtab_is_a_symlink(void) {
101
		return 1;
53
	get_mtab_info();
102
54
	return var_mtab_is_a_symlink;
103
	if (lstat(mtab_path, &mtab_stat))
104
		return 1;
105
106
	return 0;
55
}
107
}
56
108
109
/*
110
 * Tell whether or not mtab is writable (whether or not it currently exists)
111
 *
112
 * This depends on whether or not the real path is accessible and,
113
 * if so, whether or not the file can be opened.  This function
114
 * has the side effect of creating the file if it is writable.
115
 *
116
 * get_mtab_info() must have been run
117
 */
57
int
118
int
58
mtab_is_writable() {
119
mtab_is_writable() {
59
	int fd;
120
	int fd;
60
121
61
	/* Should we write to /etc/mtab upon an update?
122
	if (!mtab_is_accessible())
62
	   Probably not if it is a symlink to /proc/mounts, since that
63
	   would create a file /proc/mounts in case the proc filesystem
64
	   is not mounted. */
65
	if (mtab_is_a_symlink())
66
		return 0;
123
		return 0;
67
124
68
	fd = open(MOUNTED, O_RDWR | O_CREAT, 0644);
125
	fd = open(mtab_path, O_RDWR | O_CREAT, 0644);
69
	if (fd >= 0) {
126
	if (fd >= 0) {
70
		close(fd);
127
		close(fd);
71
		return 1;
128
		return 1;
Lines 161-181 Link Here
161
	got_mtab = 1;
218
	got_mtab = 1;
162
	mc->nxt = mc->prev = NULL;
219
	mc->nxt = mc->prev = NULL;
163
220
164
	fnam = MOUNTED;
221
	fnam = mtab_path;
165
	mfp = my_setmntent (fnam, "r");
222
	mfp = my_setmntent (fnam, "r");
166
	if (mfp == NULL || mfp->mntent_fp == NULL) {
223
	if (mfp == NULL || mfp->mntent_fp == NULL) {
167
		int errsv = errno;
224
		int errsv = errno;
168
		fnam = PROC_MOUNTS;
225
		fnam = PATH_PROC_MOUNTS;
169
		mfp = my_setmntent (fnam, "r");
226
		mfp = my_setmntent (fnam, "r");
170
		if (mfp == NULL || mfp->mntent_fp == NULL) {
227
		if (mfp == NULL || mfp->mntent_fp == NULL) {
171
			error(_("warning: can't open %s: %s"),
228
			error(_("warning: can't open %s: %s"),
172
			      MOUNTED, strerror (errsv));
229
			      mtab_path, strerror (errsv));
173
			return;
230
			return;
174
		}
231
		}
175
		if (verbose)
232
		if (verbose)
176
			printf (_("mount: could not open %s - "
233
			printf (_("mount: could not open %s - "
177
				  "using %s instead\n"),
234
				  "using %s instead\n"),
178
				MOUNTED, PROC_MOUNTS);
235
				mtab_path, PATH_PROC_MOUNTS);
179
	}
236
	}
180
	read_mntentchn(mfp, fnam, mc);
237
	read_mntentchn(mfp, fnam, mc);
181
}
238
}
Lines 482-488 Link Here
482
	if (we_created_lockfile) {
539
	if (we_created_lockfile) {
483
		close(lockfile_fd);
540
		close(lockfile_fd);
484
		lockfile_fd = -1;
541
		lockfile_fd = -1;
485
		unlink (MOUNTED_LOCK);
542
		unlink (mtab_lock_path);
486
		we_created_lockfile = 0;
543
		we_created_lockfile = 0;
487
	}
544
	}
488
}
545
}
Lines 501-512 Link Here
501
   to delete the lock afterwards. Now the use of flock() is in principle
558
   to delete the lock afterwards. Now the use of flock() is in principle
502
   superfluous, but avoids an arbitrary sleep(). */
559
   superfluous, but avoids an arbitrary sleep(). */
503
560
504
/* Where does the link point to? Obvious choices are mtab and mtab~~.
505
   HJLu points out that the latter leads to races. Right now we use
506
   mtab~.<pid> instead. Use 20 as upper bound for the length of %d. */
507
#define MOUNTLOCK_LINKTARGET		MOUNTED_LOCK "%d"
508
#define MOUNTLOCK_LINKTARGET_LTH	(sizeof(MOUNTED_LOCK)+20)
509
510
/*
561
/*
511
 * The original mount locking code has used sleep(1) between attempts and
562
 * The original mount locking code has used sleep(1) between attempts and
512
 * maximal number of attemps has been 5.
563
 * maximal number of attemps has been 5.
Lines 533-539 Link Here
533
	int i;
584
	int i;
534
	struct timespec waittime;
585
	struct timespec waittime;
535
	struct timeval maxtime;
586
	struct timeval maxtime;
536
	char linktargetfile[MOUNTLOCK_LINKTARGET_LTH];
537
587
538
	at_die = unlock_mtab;
588
	at_die = unlock_mtab;
539
589
Lines 556-573 Link Here
556
		signals_have_been_setup = 1;
606
		signals_have_been_setup = 1;
557
	}
607
	}
558
608
559
	sprintf(linktargetfile, MOUNTLOCK_LINKTARGET, getpid ());
609
	i = open (mtab_lock_targ, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
560
561
	i = open (linktargetfile, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
562
	if (i < 0) {
610
	if (i < 0) {
563
		int errsv = errno;
611
		int errsv = errno;
564
		/* linktargetfile does not exist (as a file)
612
		/* mtab_lock_targ does not exist (as a file)
565
		   and we cannot create it. Read-only filesystem?
613
		   and we cannot create it. Read-only filesystem?
566
		   Too many files open in the system?
614
		   Too many files open in the system?
567
		   Filesystem full? */
615
		   Filesystem full? */
568
		die (EX_FILEIO, _("can't create lock file %s: %s "
616
		die (EX_FILEIO, _("can't create lock file %s: %s "
569
						  "(use -n flag to override)"),
617
						  "(use -n flag to override)"),
570
			 linktargetfile, strerror (errsv));
618
			 mtab_lock_targ, strerror (errsv));
571
	}
619
	}
572
	close(i);
620
	close(i);
573
621
Lines 583-602 Link Here
583
		struct flock flock;
631
		struct flock flock;
584
		int errsv, j;
632
		int errsv, j;
585
633
586
		j = link(linktargetfile, MOUNTED_LOCK);
634
		j = link(mtab_lock_targ, mtab_lock_path);
587
		errsv = errno;
635
		errsv = errno;
588
636
589
		if (j == 0)
637
		if (j == 0)
590
			we_created_lockfile = 1;
638
			we_created_lockfile = 1;
591
639
592
		if (j < 0 && errsv != EEXIST) {
640
		if (j < 0 && errsv != EEXIST) {
593
			(void) unlink(linktargetfile);
641
			(void) unlink(mtab_lock_targ);
594
			die (EX_FILEIO, _("can't link lock file %s: %s "
642
			die (EX_FILEIO, _("can't link lock file %s: %s "
595
			     "(use -n flag to override)"),
643
			     "(use -n flag to override)"),
596
			     MOUNTED_LOCK, strerror (errsv));
644
			     mtab_lock_path, strerror (errsv));
597
		}
645
		}
598
646
599
		lockfile_fd = open (MOUNTED_LOCK, O_WRONLY);
647
		lockfile_fd = open (mtab_lock_path, O_WRONLY);
600
648
601
		if (lockfile_fd < 0) {
649
		if (lockfile_fd < 0) {
602
			/* Strange... Maybe the file was just deleted? */
650
			/* Strange... Maybe the file was just deleted? */
Lines 606-615 Link Here
606
				we_created_lockfile = 0;
654
				we_created_lockfile = 0;
607
				continue;
655
				continue;
608
			}
656
			}
609
			(void) unlink(linktargetfile);
657
			(void) unlink(mtab_lock_targ);
610
			die (EX_FILEIO, _("can't open lock file %s: %s "
658
			die (EX_FILEIO, _("can't open lock file %s: %s "
611
			     "(use -n flag to override)"),
659
			     "(use -n flag to override)"),
612
			     MOUNTED_LOCK, strerror (errsv));
660
			     mtab_lock_path, strerror (errsv));
613
		}
661
		}
614
662
615
		flock.l_type = F_WRLCK;
663
		flock.l_type = F_WRLCK;
Lines 623-633 Link Here
623
				if (verbose) {
671
				if (verbose) {
624
				    int errsv = errno;
672
				    int errsv = errno;
625
				    printf(_("Can't lock lock file %s: %s\n"),
673
				    printf(_("Can't lock lock file %s: %s\n"),
626
					   MOUNTED_LOCK, strerror (errsv));
674
					   mtab_lock_path, strerror (errsv));
627
				}
675
				}
628
				/* proceed, since it was us who created the lockfile anyway */
676
				/* proceed, since it was us who created the lockfile anyway */
629
			}
677
			}
630
			(void) unlink(linktargetfile);
678
			(void) unlink(mtab_lock_targ);
631
		} else {
679
		} else {
632
			/* Someone else made the link. Wait. */
680
			/* Someone else made the link. Wait. */
633
			gettimeofday(&now, NULL);
681
			gettimeofday(&now, NULL);
Lines 635-653 Link Here
635
				alarm(maxtime.tv_sec - now.tv_sec);
683
				alarm(maxtime.tv_sec - now.tv_sec);
636
				if (fcntl (lockfile_fd, F_SETLKW, &flock) == -1) {
684
				if (fcntl (lockfile_fd, F_SETLKW, &flock) == -1) {
637
					int errsv = errno;
685
					int errsv = errno;
638
					(void) unlink(linktargetfile);
686
					(void) unlink(mtab_lock_targ);
639
					die (EX_FILEIO, _("can't lock lock file %s: %s"),
687
					die (EX_FILEIO, _("can't lock lock file %s: %s"),
640
					     MOUNTED_LOCK, (errno == EINTR) ?
688
					     mtab_lock_path, (errno == EINTR) ?
641
					     _("timed out") : strerror (errsv));
689
					     _("timed out") : strerror (errsv));
642
				}
690
				}
643
				alarm(0);
691
				alarm(0);
644
692
645
				nanosleep(&waittime, NULL);
693
				nanosleep(&waittime, NULL);
646
			} else {
694
			} else {
647
				(void) unlink(linktargetfile);
695
				(void) unlink(mtab_lock_targ);
648
				die (EX_FILEIO, _("Cannot create link %s\n"
696
				die (EX_FILEIO, _("Cannot create link %s\n"
649
						  "Perhaps there is a stale lock file?\n"),
697
						  "Perhaps there is a stale lock file?\n"),
650
					 MOUNTED_LOCK);
698
					 mtab_lock_path);
651
			}
699
			}
652
			close(lockfile_fd);
700
			close(lockfile_fd);
653
		}
701
		}
Lines 667-682 Link Here
667
void
715
void
668
update_mtab (const char *dir, struct my_mntent *instead) {
716
update_mtab (const char *dir, struct my_mntent *instead) {
669
	mntFILE *mfp, *mftmp;
717
	mntFILE *mfp, *mftmp;
670
	const char *fnam = MOUNTED;
718
	const char *fnam = mtab_path;
671
	struct mntentchn mtabhead;	/* dummy */
719
	struct mntentchn mtabhead;	/* dummy */
672
	struct mntentchn *mc, *mc0, *absent = NULL;
720
	struct mntentchn *mc, *mc0, *absent = NULL;
673
721
674
	if (mtab_does_not_exist() || !mtab_is_writable())
722
	if (mtab_does_not_exist())
675
		return;
723
		return;
676
724
677
	lock_mtab();
725
	lock_mtab();
678
726
679
	/* having locked mtab, read it again */
727
	/* having gotten the lock, we read mtab again */
680
	mc0 = mc = &mtabhead;
728
	mc0 = mc = &mtabhead;
681
	mc->nxt = mc->prev = NULL;
729
	mc->nxt = mc->prev = NULL;
682
730
Lines 733-743 Link Here
733
	}
781
	}
734
782
735
	/* write chain to mtemp */
783
	/* write chain to mtemp */
736
	mftmp = my_setmntent (MOUNTED_TEMP, "w");
784
	mftmp = my_setmntent (mtab_temp_path, "w");
737
	if (mftmp == NULL || mftmp->mntent_fp == NULL) {
785
	if (mftmp == NULL || mftmp->mntent_fp == NULL) {
738
		int errsv = errno;
786
		int errsv = errno;
739
		error (_("cannot open %s (%s) - mtab not updated"),
787
		error (_("cannot open %s (%s) - mtab not updated"),
740
		       MOUNTED_TEMP, strerror (errsv));
788
		       mtab_temp_path, strerror (errsv));
741
		discard_mntentchn(mc0);
789
		discard_mntentchn(mc0);
742
		goto leave;
790
		goto leave;
743
	}
791
	}
Lines 746-752 Link Here
746
		if (my_addmntent(mftmp, &(mc->m)) == 1) {
794
		if (my_addmntent(mftmp, &(mc->m)) == 1) {
747
			int errsv = errno;
795
			int errsv = errno;
748
			die (EX_FILEIO, _("error writing %s: %s"),
796
			die (EX_FILEIO, _("error writing %s: %s"),
749
			     MOUNTED_TEMP, strerror (errsv));
797
			     mtab_temp_path, strerror (errsv));
750
		}
798
		}
751
	}
799
	}
752
800
Lines 756-780 Link Here
756
		    S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
804
		    S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
757
		int errsv = errno;
805
		int errsv = errno;
758
		fprintf(stderr, _("error changing mode of %s: %s\n"),
806
		fprintf(stderr, _("error changing mode of %s: %s\n"),
759
			MOUNTED_TEMP, strerror (errsv));
807
			mtab_temp_path, strerror (errsv));
760
	}
808
	}
761
	my_endmntent (mftmp);
809
	my_endmntent (mftmp);
762
810
763
	{ /*
811
	{ /*
764
	   * If mount is setuid and some non-root user mounts sth,
812
	   * If mount is setuid and some non-root user mounts sth,
765
	   * then mtab.tmp might get the group of this user. Copy uid/gid
813
	   * then the temp file might get the group of this user.
766
	   * from the present mtab before renaming.
814
	   * Copy uid/gid from the present mtab before renaming.
767
	   */
815
	   */
768
	    struct stat sbuf;
816
	    struct stat sbuf;
769
	    if (stat (MOUNTED, &sbuf) == 0)
817
	    if (stat (mtab_path, &sbuf) == 0)
770
		chown (MOUNTED_TEMP, sbuf.st_uid, sbuf.st_gid);
818
		chown (mtab_temp_path, sbuf.st_uid, sbuf.st_gid);
771
	}
819
	}
772
820
773
	/* rename mtemp to mtab */
821
	/* rename mtemp to mtab */
774
	if (rename (MOUNTED_TEMP, MOUNTED) < 0) {
822
	if (rename (mtab_temp_path, mtab_path) < 0) {
775
		int errsv = errno;
823
		int errsv = errno;
776
		fprintf(stderr, _("can't rename %s to %s: %s\n"),
824
		fprintf(stderr, _("can't rename %s to %s: %s\n"),
777
			MOUNTED_TEMP, MOUNTED, strerror(errsv));
825
			mtab_temp_path, mtab_path, strerror(errsv));
778
	}
826
	}
779
827
780
 leave:
828
 leave:
(-)util-linux-ng-2.13.1.1/mount/mount.c (-2 / +9 lines)
Lines 519-525 Link Here
519
	return ret;
519
	return ret;
520
}
520
}
521
521
522
/* Create mtab with a root entry.  */
522
/*
523
 * Create mtab with a root entry.
524
 *
525
 * Caller should check that mtab is writable first
526
 */
523
static void
527
static void
524
create_mtab (void) {
528
create_mtab (void) {
525
	struct mntentchn *fstab;
529
	struct mntentchn *fstab;
Lines 1813-1818 Link Here
1813
	initproctitle(argc, argv);
1817
	initproctitle(argc, argv);
1814
#endif
1818
#endif
1815
1819
1820
	get_mtab_info();
1821
	/* Keep in mind that /etc/mtab may be a symlink */
1822
1816
	while ((c = getopt_long (argc, argv, "afFhilL:no:O:p:rsU:vVwt:",
1823
	while ((c = getopt_long (argc, argv, "afFhilL:no:O:p:rsU:vVwt:",
1817
				 longopts, NULL)) != -1) {
1824
				 longopts, NULL)) != -1) {
1818
		switch (c) {
1825
		switch (c) {
Lines 1970-1976 Link Here
1970
			die (EX_USAGE, _("mount: only root can do that"));
1977
			die (EX_USAGE, _("mount: only root can do that"));
1971
	}
1978
	}
1972
1979
1973
	if (!nomtab && mtab_does_not_exist()) {
1980
	if (!nomtab && mtab_does_not_exist() && mtab_is_writable()) {
1974
		if (verbose > 1)
1981
		if (verbose > 1)
1975
			printf(_("mount: no %s found - creating it..\n"),
1982
			printf(_("mount: no %s found - creating it..\n"),
1976
			       MOUNTED);
1983
			       MOUNTED);
(-)util-linux-ng-2.13.1.1/mount/mount_paths.h (-1 / +6 lines)
Lines 4-10 Link Here
4
#include <mntent.h>
4
#include <mntent.h>
5
5
6
#define _PATH_FSTAB	"/etc/fstab"
6
#define _PATH_FSTAB	"/etc/fstab"
7
#define PROC_SWAPS      "/proc/swaps"
7
#define PATH_PROC	"/proc/"
8
#define PROC_SWAPS      PATH_PROC "swaps"
9
#define PATH_PROC_MOUNTS      PATH_PROC "mounts"
10
11
#define MTAB_LOCK_SUFFIX	"~"
12
#define MTAB_TEMP_SUFFIX	".tmp"
8
13
9
#ifdef _PATH_MOUNTED
14
#ifdef _PATH_MOUNTED
10
# define MOUNTED_LOCK	_PATH_MOUNTED "~"
15
# define MOUNTED_LOCK	_PATH_MOUNTED "~"
(-)util-linux-ng-2.13.1.1/mount/sundries.h (+1 lines)
Lines 21-26 Link Here
21
extern int sloppy;
21
extern int sloppy;
22
22
23
#define streq(s, t)	(strcmp ((s), (t)) == 0)
23
#define streq(s, t)	(strcmp ((s), (t)) == 0)
24
#define streqn(s, t, n)	(strncmp((s), (t), (n)) == 0)
24
25
25
/* Functions in sundries.c that are used in mount.c and umount.c  */ 
26
/* Functions in sundries.c that are used in mount.c and umount.c  */ 
26
void block_signals (int how);
27
void block_signals (int how);
(-)util-linux-ng-2.13.1.1/mount/umount.c (+2 lines)
Lines 574-579 Link Here
574
574
575
	umask(022);
575
	umask(022);
576
576
577
	get_mtab_info();
578
577
	while ((c = getopt_long (argc, argv, "adfhlnrit:O:vV",
579
	while ((c = getopt_long (argc, argv, "adfhlnrit:O:vV",
578
				 longopts, NULL)) != -1)
580
				 longopts, NULL)) != -1)
579
		switch (c) {
581
		switch (c) {

Return to bug 98403