aboutsummaryrefslogblamecommitdiffstats
path: root/ask.c
blob: 5917a12368b6489486312d2fdd10874e236cac0d (plain) (tree)
1
2
3
4
5
6
7






                                                         











































































































































                                                                                                                            




























































                                                                                                              















                                                                                                                                     












































                                                                                                                     
/* See LICENSE file for copyright and license details. */
#include "common.h"
#if defined(__linux__)
# include <sys/sysmacros.h>
#endif


struct devinfo {
	char *part;
	char *dev;
	char *part_start;
	char *part_size;
	char *dev_size;
	char *dev_wwid;
	char *dev_model;
	char *dev_serial;
	char *dev_transport;
};


static void
destroy_devinfo(struct devinfo *info)
{
	free(info->part);
	free(info->dev);
	free(info->part_start);
	free(info->part_size);
	free(info->dev_size);
	free(info->dev_wwid);
	free(info->dev_model);
	free(info->dev_serial);
	free(info->dev_transport);
}


static char *
strip(char *s)
{
	char *ret = s;
	if (s) {
		char *p = s;
		while (*s) {
			while (*s && !isspace(*s))
				s++;
			p = s;
			while (*s && isspace(*s))
				s++;
		}
		*p = '\0';
	}
	return ret;
}


static char *
fmtsize(char *s)
{
	uintmax_t val;
	char *end, *ret;
	char buf[256];

	if (!s)
		return NULL;

	errno = 0;
	val = strtoumax(s, &end, 10);
	if (!isdigit(*s) || *end || errno || val > (uintmax_t)(OFF_MAX / 512)) {
		free(s);
		return NULL;
	}

	ret = estrdup(exact_and_human_size((off_t)val * 512, buf, 1));
	free(s);
	return ret;
}


static int
get_devinfo(struct devinfo *info, const char *name)
{
	struct stat st;
	char *path, *p;
	int dfd;

	info->part = NULL;
	info->dev = NULL;
	info->part_start = NULL;
	info->part_size = NULL;
	info->dev_size = NULL;
	info->dev_wwid = NULL;
	info->dev_model = NULL;
	info->dev_serial = NULL;
	info->dev_transport = NULL;

	path = emalloc(sizeof("/sys/class/block/") + strlen(name));
	p = stpcpy(stpcpy(path, "/sys/class/block/"), name);

	dfd = open(path, O_PATH);
	if (dfd < 0)
		return 0;

	info->part = strip(readtextfileat(dfd, "partition"));
	if (info->part) {
		info->part_start = fmtsize(strip(readtextfileat(dfd, "start")));
		info->part_size = fmtsize(strip(readtextfileat(dfd, "size")));

		close(dfd);

		if (!isdigit(p[-1]))
			return 1;
		while (isdigit(p[-1]))
			*--p = '\0';

		dfd = open(path, O_PATH);
		if (dfd < 0) {
			if (isalpha(p[-1])) {
				*--p = '\0';
				dfd = open(path, O_PATH);
			}
			if (dfd < 0)
				return 1;
		}

		if (fstatat(dfd, name, &st, 0) || !S_ISDIR(st.st_mode)) {
			close(dfd);
			return 1;
		}

		while (p[-1] != '/')
			p--;
		info->dev = estrdup(p);
	}

	info->dev_size = fmtsize(strip(readtextfileat(dfd, "size")));
	info->dev_wwid = strip(readtextfileat(dfd, "wwid"));
	if (!info->dev_wwid)
		info->dev_wwid = strip(readtextfileat(dfd, "device/wwid"));
	info->dev_model = strip(readtextfileat(dfd, "device/model"));
	info->dev_serial = strip(readtextfileat(dfd, "device/serial"));
	info->dev_transport = strip(readtextfileat(dfd, "device/transport"));

	close(dfd);

	return info->part || info->dev_size || info->dev_wwid || info->dev_model || info->dev_serial || info->dev_transport;
}


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));
		struct devinfo info;
		if (devname && get_devinfo(&info, devname)) {
			if ((dprintf(ttyfd, "\nFile is identified as block device %s. Details below:\n", devname) < 0) ||
			    (info.part && info.dev && dprintf(ttyfd, "\tPartition %s of %s\n", info.part, info.dev) < 0) ||
			    (info.part_size && dprintf(ttyfd, "\tSize of the partition: %s\n", info.part_size) < 0) ||
			    (info.part_start && dprintf(ttyfd, "\tOffset of the partition: %s\n", info.part_start) < 0) ||
			    (info.dev_size && dprintf(ttyfd, "\tSize of the device: %s\n", info.dev_size) < 0) ||
			    (info.dev_model && dprintf(ttyfd, "\tModel of the device: %s\n", info.dev_model) < 0) ||
			    (info.dev_serial && dprintf(ttyfd, "\tSerial number of the device: %s\n", info.dev_serial) < 0) ||
			    (info.dev_transport && dprintf(ttyfd, "\tTransport type of the device: %s\n", info.dev_transport) < 0) ||
			    (info.dev_wwid && dprintf(ttyfd, "\tWWID of the device: %s\n", info.dev_wwid) < 0))
				goto printerror;
			destroy_devinfo(&info);
		} else if (devname) {
			if (dprintf(ttyfd, "\nFile is identified as block device %s. No details available.\n", devname) < 0)
				goto printerror;
		}
#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>");
}