aboutsummaryrefslogtreecommitdiffstats
path: root/ask.c
blob: bb8fa501236407e3d7a83b683881a97d07b03cdf (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/* See LICENSE file for copyright and license details. */
#include "common.h"
#if defined(__linux__)
# include <sys/sysmacros.h>
#endif


int
confirm(const char *device, off_t off, off_t len, off_t end)
{
	char buf[1024];
	int ttyfd = -1;
	int need_close;
	int ret = 1;
	int custom_section = (off >= 0 || len >= 0 || end >= 0);
	const char *type;
	ssize_t i, r;
	int confirm_state = 0;
	struct stat st;

	if (isatty(STDIN_FILENO)) {
		ttyfd = STDIN_FILENO;
		need_close = 0;
	} else {
		ttyfd = open("/dev/tty", O_RDWR);
		if (ttyfd < 0)
			return 1;
		need_close = 1;
	}

	if (getpgrp() != tcgetpgrp(ttyfd))
		goto out;

	if (custom_section) {
		if (off < 0)
			off = 0;
		if (end >= 0)
			len = end - off;
		else if (len >= 0)
			end = off + len;
		else if (off == 0)
			custom_section = 0;
		if (end >= 0 && end < off)
			eprintf("the value of -e option is lower than value of -o option");
	}

	if (stat(device, &st))
		eprintf("stat %s:", device);
	switch (st.st_mode & S_IFMT) {
	case S_IFREG:
		type = "file";
		break;
	case S_IFBLK:
		type = "device";
		break;
	default:
		eprintf("%s: not a regular file or block device", device);
	}

	if (dprintf(ttyfd, "Are you sure you want to erase the contents of %s?\n", device) < 0)
		goto printerror;

	if (S_ISREG(st.st_mode)) {
		if (dprintf(ttyfd, "\nThe file is %s large.\n", exact_and_human_size(st.st_size, buf, 1)) < 0)
			goto printerror;
#if defined(__linux__)
	} else if (S_ISBLK(st.st_mode)) {
		char *devname = get_device_name(major(st.st_rdev), minor(st.st_rdev));
		if (devname) {
			/* TODO display device identity and size (look in /sys/class/block/<devname>/) */
		}
#endif
	}

	if (custom_section) {
		if (dprintf(ttyfd, "\nYou have requested to only erase part of the %s:\n", type) < 0 ||
		    (off >= 0 && dprintf(ttyfd, "\tFirst byte: %s\n", exact_and_human_size(off, buf, 0)) < 0) ||
		    (len >= 0 && dprintf(ttyfd, "\tNumber of bytes: %s\n", exact_and_human_size(len, buf, 0)) < 0) ||
		    (end >= 0 && dprintf(ttyfd, "\tEnd of overwrite: %s\n", exact_and_human_size(end, buf, 1)) < 0))
			goto printerror;
	}

	if (dprintf(ttyfd, "\nAffirm by entering 'yes' in majuscule without quotes.\n") < 0)
		goto printerror;

	ret = 0;
	for (;;) {
		r = read(ttyfd, buf, sizeof(buf));
		if (!r) {
			goto out;
		} else if (r < 0) {
			if (errno == EINTR)
				continue;
			eprintf("read %s:", need_close ? "/dev/tty" : "<stdin>");
		}
		for (i = 0; i < r; i++) {
			if (buf[i] != "YES\n"[confirm_state++])
				goto out;
			if (buf[i] == '\n') {
				ret = 1;
				goto out;
			}
		}
	}

out:
	if (need_close)
		close(ttyfd);
	if (dprintf(ttyfd, "%s\n", ret ? "" : "User did not affirm action. Aborting...") < 0)
		goto printerror;
	return ret;

printerror:
	eprintf("dprintf %s:", need_close ? "/dev/tty" : "<stdin>");
}