1/*
2 * e2initrd_helper.c - Get the filesystem table
3 *
4 * Copyright 2004 by Theodore Ts'o.
5 *
6 * %Begin-Header%
7 * This file may be redistributed under the terms of the GNU Public
8 * License.
9 * %End-Header%
10 */
11
12#include "config.h"
13#include <stdio.h>
14#include <unistd.h>
15#ifdef HAVE_STDLIB_H
16#include <stdlib.h>
17#endif
18#include <ctype.h>
19#include <string.h>
20#include <time.h>
21#ifdef HAVE_ERRNO_H
22#include <errno.h>
23#endif
24#include <sys/types.h>
25#include <sys/stat.h>
26#include <fcntl.h>
27#include <utime.h>
28#ifdef HAVE_GETOPT_H
29#include <getopt.h>
30#else
31extern int optind;
32extern char *optarg;
33#endif
34
35#include "ext2fs/ext2_fs.h"
36#include "ext2fs/ext2fs.h"
37#include "blkid/blkid.h"
38#include "support/nls-enable.h"
39
40#include "../version.h"
41
42static const char * program_name = "e2initrd_helper";
43static char * device_name;
44static int open_flag;
45static int root_type;
46static blkid_cache cache = NULL;
47
48struct mem_file {
49	char	*buf;
50	int	size;
51	int	ptr;
52};
53
54struct fs_info {
55	char  *device;
56	char  *mountpt;
57	char  *type;
58	char  *opts;
59	int   freq;
60	int   passno;
61	int   flags;
62	struct fs_info *next;
63};
64
65static void usage(void)
66{
67	fprintf(stderr,
68		_("Usage: %s -r device\n"), program_name);
69	exit (1);
70}
71
72static errcode_t get_file(ext2_filsys fs, const char * filename,
73		   struct mem_file *ret_file)
74{
75	errcode_t	retval;
76	char 		*buf;
77	ext2_file_t	e2_file = NULL;
78	unsigned int	got;
79	struct ext2_inode inode;
80	ext2_ino_t	ino;
81
82	ret_file->buf = 0;
83	ret_file->size = 0;
84	ret_file->ptr = 0;
85
86	retval = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO,
87			      filename, &ino);
88	if (retval)
89		return retval;
90
91	retval = ext2fs_read_inode(fs, ino, &inode);
92	if (retval)
93		return retval;
94
95	if (inode.i_size_high || (inode.i_size > 65536))
96		return EFBIG;
97
98	buf = malloc(inode.i_size + 1);
99	if (!buf)
100		return ENOMEM;
101	memset(buf, 0, inode.i_size+1);
102
103	retval = ext2fs_file_open(fs, ino, 0, &e2_file);
104	if (retval)
105		goto errout;
106
107	retval = ext2fs_file_read(e2_file, buf, inode.i_size, &got);
108	if (retval)
109		goto errout;
110
111	retval = ext2fs_file_close(e2_file);
112	if (retval)
113		goto errout;
114
115	ret_file->buf = buf;
116	ret_file->size = (int) got;
117	return 0;
118
119errout:
120	free(buf);
121	if (e2_file)
122		ext2fs_file_close(e2_file);
123	return retval;
124}
125
126static char *get_line(struct mem_file *file)
127{
128	char	*cp, *ret;
129	int	s = 0;
130
131	cp = file->buf + file->ptr;
132	while (*cp && *cp != '\n') {
133		cp++;
134		s++;
135	}
136	ret = malloc(s+1);
137	if (!ret)
138		return 0;
139	ret[s]=0;
140	memcpy(ret, file->buf + file->ptr, s);
141	while (*cp && (*cp == '\n' || *cp == '\r')) {
142		cp++;
143		s++;
144	}
145	file->ptr += s;
146	return ret;
147}
148
149static int mem_file_eof(struct mem_file *file)
150{
151	return (file->ptr >= file->size);
152}
153
154/*
155 * fstab parsing code
156 */
157static char *string_copy(const char *s)
158{
159	char	*ret;
160
161	if (!s)
162		return 0;
163	ret = malloc(strlen(s)+1);
164	if (ret)
165		strcpy(ret, s);
166	return ret;
167}
168
169static char *skip_over_blank(char *cp)
170{
171	while (*cp && isspace(*cp))
172		cp++;
173	return cp;
174}
175
176static char *skip_over_word(char *cp)
177{
178	while (*cp && !isspace(*cp))
179		cp++;
180	return cp;
181}
182
183static char *parse_word(char **buf)
184{
185	char *word, *next;
186
187	word = *buf;
188	if (*word == 0)
189		return 0;
190
191	word = skip_over_blank(word);
192	next = skip_over_word(word);
193	if (*next)
194		*next++ = 0;
195	*buf = next;
196	return word;
197}
198
199static void parse_escape(char *word)
200{
201	char	*p, *q;
202	int	ac, i;
203
204	if (!word)
205		return;
206
207	for (p = word, q = word; *p; p++, q++) {
208		*q = *p;
209		if (*p != '\\')
210			continue;
211		if (*++p == 0)
212			break;
213		if (*p == 't') {
214			*q = '\t';
215			continue;
216		}
217		if (*p == 'n') {
218			*q = '\n';
219			continue;
220		}
221		if (!isdigit(*p)) {
222			*q = *p;
223			continue;
224		}
225		ac = 0;
226		for (i = 0; i < 3; i++, p++) {
227			if (!isdigit(*p))
228				break;
229			ac = (ac * 8) + (*p - '0');
230		}
231		*q = ac;
232		p--;
233	}
234	*q = 0;
235}
236
237static int parse_fstab_line(char *line, struct fs_info *fs)
238{
239	char	*dev, *device, *mntpnt, *type, *opts, *freq, *passno, *cp;
240
241	if ((cp = strchr(line, '#')))
242		*cp = 0;	/* Ignore everything after the comment char */
243	cp = line;
244
245	device = parse_word(&cp);
246	mntpnt = parse_word(&cp);
247	type = parse_word(&cp);
248	opts = parse_word(&cp);
249	freq = parse_word(&cp);
250	passno = parse_word(&cp);
251
252	if (!device)
253		return -1;	/* Allow blank lines */
254
255	if (!mntpnt || !type)
256		return -1;
257
258	parse_escape(device);
259	parse_escape(mntpnt);
260	parse_escape(type);
261	parse_escape(opts);
262	parse_escape(freq);
263	parse_escape(passno);
264
265	dev = blkid_get_devname(cache, device, NULL);
266	if (dev)
267		device = dev;
268
269	if (strchr(type, ','))
270		type = 0;
271
272	fs->device = string_copy(device);
273	fs->mountpt = string_copy(mntpnt);
274	fs->type = string_copy(type);
275	fs->opts = string_copy(opts ? opts : "");
276	fs->freq = freq ? atoi(freq) : -1;
277	fs->passno = passno ? atoi(passno) : -1;
278	fs->flags = 0;
279	fs->next = NULL;
280
281	free(dev);
282
283	return 0;
284}
285
286static void free_fstab_line(struct fs_info *fs)
287{
288	if (fs->device)
289		fs->device = 0;
290	if (fs->mountpt)
291		fs->mountpt = 0;
292	if (fs->type)
293		fs->type = 0;
294	if (fs->opts)
295		fs->opts = 0;
296	memset(fs, 0, sizeof(struct fs_info));
297}
298
299
300static void PRS(int argc, char **argv)
301{
302	int c;
303
304#ifdef ENABLE_NLS
305	setlocale(LC_MESSAGES, "");
306	setlocale(LC_CTYPE, "");
307	bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
308	textdomain(NLS_CAT_NAME);
309	set_com_err_gettext(gettext);
310#endif
311
312	while ((c = getopt(argc, argv, "rv")) != EOF) {
313		switch (c) {
314		case 'r':
315			root_type++;
316			break;
317
318		case 'v':
319			printf("%s %s (%s)\n", program_name,
320			       E2FSPROGS_VERSION, E2FSPROGS_DATE);
321			break;
322		default:
323			usage();
324		}
325	}
326	if (optind < argc - 1 || optind == argc)
327		usage();
328	device_name = blkid_get_devname(NULL, argv[optind], NULL);
329	if (!device_name) {
330		com_err(program_name, 0, _("Unable to resolve '%s'"),
331			argv[optind]);
332		exit(1);
333	}
334}
335
336static void get_root_type(ext2_filsys fs)
337{
338	errcode_t retval;
339	struct mem_file file;
340	char 		*buf;
341	struct fs_info fs_info;
342	int		ret;
343
344	retval = get_file(fs, "/etc/fstab", &file);
345	if (retval) {
346		com_err(program_name, retval, "couldn't open /etc/fstab");
347		exit(1);
348	}
349
350	while (!mem_file_eof(&file)) {
351		buf = get_line(&file);
352		if (!buf)
353			continue;
354
355		ret = parse_fstab_line(buf, &fs_info);
356		if (ret < 0)
357			goto next_line;
358
359		if (!strcmp(fs_info.mountpt, "/"))
360			printf("%s\n", fs_info.type);
361
362		free_fstab_line(&fs_info);
363
364	next_line:
365		free(buf);
366	}
367}
368
369
370int main (int argc, char ** argv)
371{
372	errcode_t retval;
373	ext2_filsys fs;
374	io_manager io_ptr;
375
376	add_error_table(&et_ext2_error_table);
377
378	blkid_get_cache(&cache, NULL);
379	PRS(argc, argv);
380
381#ifdef CONFIG_TESTIO_DEBUG
382	if (getenv("TEST_IO_FLAGS") || getenv("TEST_IO_BLOCK")) {
383		io_ptr = test_io_manager;
384		test_io_backing_manager = unix_io_manager;
385	} else
386#endif
387		io_ptr = unix_io_manager;
388	retval = ext2fs_open (device_name, open_flag, 0, 0, io_ptr, &fs);
389        if (retval)
390		exit(1);
391
392	if (root_type)
393		get_root_type(fs);
394
395	remove_error_table(&et_ext2_error_table);
396	return (ext2fs_close (fs) ? 1 : 0);
397}
398