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
31 extern int optind;
32 extern 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 
42 static const char * program_name = "e2initrd_helper";
43 static char * device_name;
44 static int open_flag;
45 static int root_type;
46 static blkid_cache cache = NULL;
47 
48 struct mem_file {
49 	char	*buf;
50 	int	size;
51 	int	ptr;
52 };
53 
54 struct 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 
65 static void usage(void)
66 {
67 	fprintf(stderr,
68 		_("Usage: %s -r device\n"), program_name);
69 	exit (1);
70 }
71 
72 static 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 
119 errout:
120 	free(buf);
121 	if (e2_file)
122 		ext2fs_file_close(e2_file);
123 	return retval;
124 }
125 
126 static 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 
149 static int mem_file_eof(struct mem_file *file)
150 {
151 	return (file->ptr >= file->size);
152 }
153 
154 /*
155  * fstab parsing code
156  */
157 static 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 
169 static char *skip_over_blank(char *cp)
170 {
171 	while (*cp && isspace(*cp))
172 		cp++;
173 	return cp;
174 }
175 
176 static char *skip_over_word(char *cp)
177 {
178 	while (*cp && !isspace(*cp))
179 		cp++;
180 	return cp;
181 }
182 
183 static 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 
199 static 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 
237 static 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 
286 static 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 
300 static 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 
336 static 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 
370 int 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