/* 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>");
}