/* See LICENSE file for copyright and license details. */ #include "common.h" #if defined(__linux__) # include #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" : ""); } 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" : ""); }