GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/file/file.c Lines: 0 267 0.0 %
Date: 2017-11-07 Branches: 0 212 0.0 %

Line Branch Exec Source
1
/* $OpenBSD: file.c,v 1.64 2017/07/01 21:07:13 brynet Exp $ */
2
3
/*
4
 * Copyright (c) 2015 Nicholas Marriott <nicm@openbsd.org>
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
19
#include <sys/types.h>
20
#include <sys/mman.h>
21
#include <sys/stat.h>
22
23
#include <err.h>
24
#include <errno.h>
25
#include <fcntl.h>
26
#include <getopt.h>
27
#include <libgen.h>
28
#include <limits.h>
29
#include <pwd.h>
30
#include <stdlib.h>
31
#include <string.h>
32
#include <time.h>
33
#include <unistd.h>
34
35
#include "file.h"
36
#include "magic.h"
37
#include "xmalloc.h"
38
39
struct input_file {
40
	struct magic	*m;
41
42
	const char	*path;
43
	struct stat	 sb;
44
	int		 fd;
45
	int		 error;
46
47
	char		 link_path[PATH_MAX];
48
	int		 link_error;
49
	int		 link_target;
50
51
	void		*base;
52
	size_t		 size;
53
	int		 mapped;
54
	char		*result;
55
};
56
57
extern char	*__progname;
58
59
__dead void	 usage(void);
60
61
static void	 prepare_input(struct input_file *, const char *);
62
63
static void	 read_link(struct input_file *, const char *);
64
65
static void	 test_file(struct input_file *, size_t);
66
67
static int	 try_stat(struct input_file *);
68
static int	 try_empty(struct input_file *);
69
static int	 try_access(struct input_file *);
70
static int	 try_text(struct input_file *);
71
static int	 try_magic(struct input_file *);
72
static int	 try_unknown(struct input_file *);
73
74
static int	 bflag;
75
static int	 cflag;
76
static int	 iflag;
77
static int	 Lflag;
78
static int	 sflag;
79
static int	 Wflag;
80
81
static struct option longopts[] = {
82
	{ "brief",       no_argument, NULL, 'b' },
83
	{ "dereference", no_argument, NULL, 'L' },
84
	{ "mime",        no_argument, NULL, 'i' },
85
	{ "mime-type",   no_argument, NULL, 'i' },
86
	{ NULL,          0,           NULL, 0   }
87
};
88
89
__dead void
90
usage(void)
91
{
92
	fprintf(stderr, "usage: %s [-bchiLsW] file ...\n", __progname);
93
	exit(1);
94
}
95
96
int
97
main(int argc, char **argv)
98
{
99
	int			 opt, idx;
100
	char			*home, *magicpath;
101
	struct passwd		*pw;
102
	FILE			*magicfp = NULL;
103
	struct magic		*m;
104
	struct input_file	*inf = NULL;
105
	size_t			 len, width = 0;
106
107
	if (pledge("stdio rpath getpw id flock cpath wpath", NULL) == -1)
108
		err(1, "pledge");
109
110
	for (;;) {
111
		opt = getopt_long(argc, argv, "bchiLsW", longopts, NULL);
112
		if (opt == -1)
113
			break;
114
		switch (opt) {
115
		case 'b':
116
			bflag = 1;
117
			break;
118
		case 'c':
119
			cflag = 1;
120
			break;
121
		case 'h':
122
			Lflag = 0;
123
			break;
124
		case 'i':
125
			iflag = 1;
126
			break;
127
		case 'L':
128
			Lflag = 1;
129
			break;
130
		case 's':
131
			sflag = 1;
132
			break;
133
		case 'W':
134
			Wflag = 1;
135
			break;
136
		default:
137
			usage();
138
		}
139
	}
140
	argc -= optind;
141
	argv += optind;
142
	if (cflag) {
143
		if (argc != 0)
144
			usage();
145
	} else if (argc == 0)
146
		usage();
147
148
	if (geteuid() != 0 && !issetugid()) {
149
		home = getenv("HOME");
150
		if (home == NULL || *home == '\0') {
151
			pw = getpwuid(getuid());
152
			if (pw != NULL)
153
				home = pw->pw_dir;
154
			else
155
				home = NULL;
156
		}
157
		if (home != NULL) {
158
			xasprintf(&magicpath, "%s/.magic", home);
159
			magicfp = fopen(magicpath, "r");
160
			if (magicfp == NULL)
161
				free(magicpath);
162
		}
163
	}
164
	if (magicfp == NULL) {
165
		magicpath = xstrdup("/etc/magic");
166
		magicfp = fopen(magicpath, "r");
167
	}
168
	if (magicfp == NULL)
169
		err(1, "%s", magicpath);
170
171
	if (!cflag) {
172
		inf = xcalloc(argc, sizeof *inf);
173
		for (idx = 0; idx < argc; idx++) {
174
			len = strlen(argv[idx]) + 1;
175
			if (len > width)
176
				width = len;
177
			prepare_input(&inf[idx], argv[idx]);
178
		}
179
	}
180
181
	tzset();
182
183
	if (pledge("stdio getpw id flock rpath cpath wpath", NULL) == -1)
184
		err(1, "pledge");
185
186
	if (geteuid() == 0) {
187
		pw = getpwnam(FILE_USER);
188
		if (pw == NULL)
189
			errx(1, "unknown user %s", FILE_USER);
190
		if (setgroups(1, &pw->pw_gid) != 0)
191
			err(1, "setgroups");
192
		if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0)
193
			err(1, "setresgid");
194
		if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0)
195
			err(1, "setresuid");
196
	}
197
198
	if (pledge("stdio flock rpath cpath wpath", NULL) == -1)
199
		err(1, "pledge");
200
201
	m = magic_load(magicfp, magicpath, cflag || Wflag);
202
	if (cflag) {
203
		magic_dump(m);
204
		exit(0);
205
	}
206
	fclose(magicfp);
207
208
	for (idx = 0; idx < argc; idx++) {
209
		inf[idx].m = m;
210
		test_file(&inf[idx], width);
211
		if (inf[idx].fd != -1 && inf[idx].fd != STDIN_FILENO)
212
			close(inf[idx].fd);
213
	}
214
	exit(0);
215
}
216
217
static void
218
prepare_input(struct input_file *inf, const char *path)
219
{
220
	int	fd, mode, error;
221
222
	inf->path = path;
223
224
	if (strcmp(path, "-") == 0) {
225
		if (fstat(STDIN_FILENO, &inf->sb) == -1) {
226
			inf->error = errno;
227
			inf->fd = -1;
228
			return;
229
		}
230
		inf->fd = STDIN_FILENO;
231
		return;
232
	}
233
234
	if (Lflag)
235
		error = stat(path, &inf->sb);
236
	else
237
		error = lstat(path, &inf->sb);
238
	if (error == -1) {
239
		inf->error = errno;
240
		inf->fd = -1;
241
		return;
242
	}
243
244
	/* We don't need them, so don't open directories or symlinks. */
245
	mode = inf->sb.st_mode;
246
	if (!S_ISDIR(mode) && !S_ISLNK(mode)) {
247
		fd = open(path, O_RDONLY|O_NONBLOCK);
248
		if (fd == -1 && (errno == ENFILE || errno == EMFILE))
249
			err(1, "open");
250
	} else
251
		fd = -1;
252
	if (S_ISLNK(mode))
253
		read_link(inf, path);
254
	inf->fd = fd;
255
}
256
257
static void
258
read_link(struct input_file *inf, const char *path)
259
{
260
	struct stat	 sb;
261
	char		 lpath[PATH_MAX];
262
	char		*copy, *root;
263
	int		 used;
264
	ssize_t		 size;
265
266
	size = readlink(path, lpath, sizeof lpath - 1);
267
	if (size == -1) {
268
		inf->link_error = errno;
269
		return;
270
	}
271
	lpath[size] = '\0';
272
273
	if (*lpath == '/')
274
		strlcpy(inf->link_path, lpath, sizeof inf->link_path);
275
	else {
276
		copy = xstrdup(path);
277
278
		root = dirname(copy);
279
		if (*root == '\0' || strcmp(root, ".") == 0 ||
280
		    strcmp (root, "/") == 0)
281
			strlcpy(inf->link_path, lpath, sizeof inf->link_path);
282
		else {
283
			used = snprintf(inf->link_path, sizeof inf->link_path,
284
			    "%s/%s", root, lpath);
285
			if (used < 0 || (size_t)used >= sizeof inf->link_path) {
286
				inf->link_error = ENAMETOOLONG;
287
				free(copy);
288
				return;
289
			}
290
		}
291
292
		free(copy);
293
	}
294
295
	if (!Lflag && stat(path, &sb) == -1)
296
		inf->link_target = errno;
297
}
298
299
static void *
300
fill_buffer(int fd, size_t size, size_t *used)
301
{
302
	static void	*buffer;
303
	ssize_t		 got;
304
	size_t		 left;
305
	void		*next;
306
307
	if (buffer == NULL)
308
		buffer = xmalloc(FILE_READ_SIZE);
309
310
	next = buffer;
311
	left = size;
312
	while (left != 0) {
313
		got = read(fd, next, left);
314
		if (got == -1) {
315
			if (errno == EINTR)
316
				continue;
317
			return (NULL);
318
		}
319
		if (got == 0)
320
			break;
321
		next = (char *)next + got;
322
		left -= got;
323
	}
324
	*used = size - left;
325
	return (buffer);
326
}
327
328
static int
329
load_file(struct input_file *inf)
330
{
331
	size_t	used;
332
333
	if (inf->sb.st_size == 0 && S_ISREG(inf->sb.st_mode))
334
		return (0); /* empty file */
335
	if (inf->sb.st_size == 0 || inf->sb.st_size > FILE_READ_SIZE)
336
		inf->size = FILE_READ_SIZE;
337
	else
338
		inf->size = inf->sb.st_size;
339
340
	if (!S_ISREG(inf->sb.st_mode))
341
		goto try_read;
342
343
	inf->base = mmap(NULL, inf->size, PROT_READ, MAP_PRIVATE, inf->fd, 0);
344
	if (inf->base == MAP_FAILED)
345
		goto try_read;
346
	inf->mapped = 1;
347
	return (0);
348
349
try_read:
350
	inf->base = fill_buffer(inf->fd, inf->size, &used);
351
	if (inf->base == NULL) {
352
		xasprintf(&inf->result, "cannot read '%s' (%s)", inf->path,
353
		    strerror(errno));
354
		return (1);
355
	}
356
	inf->size = used;
357
	return (0);
358
}
359
360
static int
361
try_stat(struct input_file *inf)
362
{
363
	if (inf->error != 0) {
364
		xasprintf(&inf->result, "cannot stat '%s' (%s)", inf->path,
365
		    strerror(inf->error));
366
		return (1);
367
	}
368
	if (sflag || strcmp(inf->path, "-") == 0) {
369
		switch (inf->sb.st_mode & S_IFMT) {
370
		case S_IFIFO:
371
			if (strcmp(inf->path, "-") != 0)
372
				break;
373
		case S_IFBLK:
374
		case S_IFCHR:
375
		case S_IFREG:
376
			return (0);
377
		}
378
	}
379
380
	if (iflag && (inf->sb.st_mode & S_IFMT) != S_IFREG) {
381
		xasprintf(&inf->result, "application/x-not-regular-file");
382
		return (1);
383
	}
384
385
	switch (inf->sb.st_mode & S_IFMT) {
386
	case S_IFDIR:
387
		xasprintf(&inf->result, "directory");
388
		return (1);
389
	case S_IFLNK:
390
		if (inf->link_error != 0) {
391
			xasprintf(&inf->result, "unreadable symlink '%s' (%s)",
392
			    inf->path, strerror(inf->link_error));
393
			return (1);
394
		}
395
		if (inf->link_target == ELOOP)
396
			xasprintf(&inf->result, "symbolic link in a loop");
397
		else if (inf->link_target != 0) {
398
			xasprintf(&inf->result, "broken symbolic link to '%s'",
399
			    inf->link_path);
400
		} else {
401
			xasprintf(&inf->result, "symbolic link to '%s'",
402
			    inf->link_path);
403
		}
404
		return (1);
405
	case S_IFSOCK:
406
		xasprintf(&inf->result, "socket");
407
		return (1);
408
	case S_IFBLK:
409
		xasprintf(&inf->result, "block special (%ld/%ld)",
410
		    (long)major(inf->sb.st_rdev),
411
		    (long)minor(inf->sb.st_rdev));
412
		return (1);
413
	case S_IFCHR:
414
		xasprintf(&inf->result, "character special (%ld/%ld)",
415
		    (long)major(inf->sb.st_rdev),
416
		    (long)minor(inf->sb.st_rdev));
417
		return (1);
418
	case S_IFIFO:
419
		xasprintf(&inf->result, "fifo (named pipe)");
420
		return (1);
421
	}
422
	return (0);
423
}
424
425
static int
426
try_empty(struct input_file *inf)
427
{
428
	if (inf->size != 0)
429
		return (0);
430
431
	if (iflag)
432
		xasprintf(&inf->result, "application/x-empty");
433
	else
434
		xasprintf(&inf->result, "empty");
435
	return (1);
436
}
437
438
static int
439
try_access(struct input_file *inf)
440
{
441
	char tmp[256] = "";
442
443
	if (inf->sb.st_size == 0 && S_ISREG(inf->sb.st_mode))
444
		return (0); /* empty file */
445
	if (inf->fd != -1)
446
		return (0);
447
448
	if (inf->sb.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH))
449
		strlcat(tmp, "writable, ", sizeof tmp);
450
	if (inf->sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))
451
		strlcat(tmp, "executable, ", sizeof tmp);
452
	if (S_ISREG(inf->sb.st_mode))
453
		strlcat(tmp, "regular file, ", sizeof tmp);
454
	strlcat(tmp, "no read permission", sizeof tmp);
455
456
	inf->result = xstrdup(tmp);
457
	return (1);
458
}
459
460
static int
461
try_text(struct input_file *inf)
462
{
463
	const char	*type, *s;
464
	int		 flags;
465
466
	flags = MAGIC_TEST_TEXT;
467
	if (iflag)
468
		flags |= MAGIC_TEST_MIME;
469
470
	type = text_get_type(inf->base, inf->size);
471
	if (type == NULL)
472
		return (0);
473
474
	s = magic_test(inf->m, inf->base, inf->size, flags);
475
	if (s != NULL) {
476
		inf->result = xstrdup(s);
477
		return (1);
478
	}
479
480
	s = text_try_words(inf->base, inf->size, flags);
481
	if (s != NULL) {
482
		if (iflag)
483
			inf->result = xstrdup(s);
484
		else
485
			xasprintf(&inf->result, "%s %s text", type, s);
486
		return (1);
487
	}
488
489
	if (iflag)
490
		inf->result = xstrdup("text/plain");
491
	else
492
		xasprintf(&inf->result, "%s text", type);
493
	return (1);
494
}
495
496
static int
497
try_magic(struct input_file *inf)
498
{
499
	const char	*s;
500
	int		 flags;
501
502
	flags = 0;
503
	if (iflag)
504
		flags |= MAGIC_TEST_MIME;
505
506
	s = magic_test(inf->m, inf->base, inf->size, flags);
507
	if (s != NULL) {
508
		inf->result = xstrdup(s);
509
		return (1);
510
	}
511
	return (0);
512
}
513
514
static int
515
try_unknown(struct input_file *inf)
516
{
517
	if (iflag)
518
		xasprintf(&inf->result, "application/x-not-regular-file");
519
	else
520
		xasprintf(&inf->result, "data");
521
	return (1);
522
}
523
524
static void
525
test_file(struct input_file *inf, size_t width)
526
{
527
	char	*label;
528
	int	 stop;
529
530
	stop = 0;
531
	if (!stop)
532
		stop = try_stat(inf);
533
	if (!stop)
534
		stop = try_access(inf);
535
	if (!stop)
536
		stop = load_file(inf);
537
	if (!stop)
538
		stop = try_empty(inf);
539
	if (!stop)
540
		stop = try_magic(inf);
541
	if (!stop)
542
		stop = try_text(inf);
543
	if (!stop)
544
		stop = try_unknown(inf);
545
546
	if (bflag)
547
		printf("%s\n", inf->result);
548
	else {
549
		if (strcmp(inf->path, "-") == 0)
550
			xasprintf(&label, "/dev/stdin:");
551
		else
552
			xasprintf(&label, "%s:", inf->path);
553
		printf("%-*s %s\n", (int)width, label, inf->result);
554
		free(label);
555
	}
556
	free(inf->result);
557
558
	if (inf->mapped && inf->base != NULL)
559
		munmap(inf->base, inf->size);
560
}