summaryrefslogtreecommitdiffstats
path: root/libblkid/src/probe.c
diff options
context:
space:
mode:
Diffstat (limited to 'libblkid/src/probe.c')
-rw-r--r--libblkid/src/probe.c213
1 files changed, 185 insertions, 28 deletions
diff --git a/libblkid/src/probe.c b/libblkid/src/probe.c
index fbf504a..76905e1 100644
--- a/libblkid/src/probe.c
+++ b/libblkid/src/probe.c
@@ -90,6 +90,7 @@
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
+#include <sys/mman.h>
#include <sys/types.h>
#ifdef HAVE_LINUX_CDROM_H
#include <linux/cdrom.h>
@@ -155,6 +156,7 @@ blkid_probe blkid_new_probe(void)
pr->chains[i].enabled = chains_drvs[i]->dflt_enabled;
}
INIT_LIST_HEAD(&pr->buffers);
+ INIT_LIST_HEAD(&pr->prunable_buffers);
INIT_LIST_HEAD(&pr->values);
INIT_LIST_HEAD(&pr->hints);
return pr;
@@ -182,6 +184,7 @@ blkid_probe blkid_clone_probe(blkid_probe parent)
pr->fd = parent->fd;
pr->off = parent->off;
pr->size = parent->size;
+ pr->io_size = parent->io_size;
pr->devno = parent->devno;
pr->disk_devno = parent->disk_devno;
pr->blkssz = parent->blkssz;
@@ -543,6 +546,16 @@ int __blkid_probe_filter_types(blkid_probe pr, int chain, int flag, char *names[
return 0;
}
+static void remove_buffer(struct blkid_bufinfo *bf)
+{
+ list_del(&bf->bufs);
+
+ DBG(BUFFER, ul_debug(" remove buffer: [off=%"PRIu64", len=%"PRIu64"]",
+ bf->off, bf->len));
+ munmap(bf->data, bf->len);
+ free(bf);
+}
+
static struct blkid_bufinfo *read_buffer(blkid_probe pr, uint64_t real_off, uint64_t len)
{
ssize_t ret;
@@ -560,13 +573,20 @@ static struct blkid_bufinfo *read_buffer(blkid_probe pr, uint64_t real_off, uint
}
/* allocate info and space for data by one malloc call */
- bf = calloc(1, sizeof(struct blkid_bufinfo) + len);
+ bf = calloc(1, sizeof(struct blkid_bufinfo));
if (!bf) {
errno = ENOMEM;
return NULL;
}
- bf->data = ((unsigned char *) bf) + sizeof(struct blkid_bufinfo);
+ bf->data = mmap(NULL, len, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (bf->data == MAP_FAILED) {
+ free(bf);
+ errno = ENOMEM;
+ return NULL;
+ }
+
bf->len = len;
bf->off = real_off;
INIT_LIST_HEAD(&bf->bufs);
@@ -577,7 +597,7 @@ static struct blkid_bufinfo *read_buffer(blkid_probe pr, uint64_t real_off, uint
ret = read(pr->fd, bf->data, len);
if (ret != (ssize_t) len) {
DBG(LOWPROBE, ul_debug("\tread failed: %m"));
- free(bf);
+ remove_buffer(bf);
/* I/O errors on CDROMs are non-fatal to work with hybrid
* audio+data disks */
@@ -601,6 +621,9 @@ static struct blkid_bufinfo *read_buffer(blkid_probe pr, uint64_t real_off, uint
return NULL;
}
+ if (mprotect(bf->data, len, PROT_READ))
+ DBG(LOWPROBE, ul_debug("\tmprotect failed: %m"));
+
return bf;
}
@@ -626,6 +649,39 @@ static struct blkid_bufinfo *get_cached_buffer(blkid_probe pr, uint64_t off, uin
}
/*
+ * Mark smaller buffers that can be satisfied by bf as prunable
+ */
+static void mark_prunable_buffers(blkid_probe pr, const struct blkid_bufinfo *bf)
+{
+ struct list_head *p, *next;
+
+ list_for_each_safe(p, next, &pr->buffers) {
+ struct blkid_bufinfo *x =
+ list_entry(p, struct blkid_bufinfo, bufs);
+
+ if (bf->off <= x->off && bf->off + bf->len >= x->off + x->len) {
+ list_del(&x->bufs);
+ list_add(&x->bufs, &pr->prunable_buffers);
+ }
+ }
+}
+
+/*
+ * Remove buffers that are marked as prunable
+ */
+void blkid_probe_prune_buffers(blkid_probe pr)
+{
+ struct list_head *p, *next;
+
+ list_for_each_safe(p, next, &pr->prunable_buffers) {
+ struct blkid_bufinfo *x =
+ list_entry(p, struct blkid_bufinfo, bufs);
+
+ remove_buffer(x);
+ }
+}
+
+/*
* Zeroize in-memory data in already read buffer. The next blkid_probe_get_buffer()
* will return modified buffer. This is usable when you want to call the same probing
* function more than once and hide previously detected magic strings.
@@ -657,7 +713,9 @@ static int hide_buffer(blkid_probe pr, uint64_t off, uint64_t len)
DBG(BUFFER, ul_debug("\thiding: off=%"PRIu64" len=%"PRIu64,
off, len));
+ mprotect(x->data, x->len, PROT_READ | PROT_WRITE);
memset(data, 0, len);
+ mprotect(x->data, x->len, PROT_READ);
ct++;
}
}
@@ -669,16 +727,29 @@ static int hide_buffer(blkid_probe pr, uint64_t off, uint64_t len)
* Note that @off is offset within probing area, the probing area is defined by
* pr->off and pr->size.
*/
-unsigned char *blkid_probe_get_buffer(blkid_probe pr, uint64_t off, uint64_t len)
+const unsigned char *blkid_probe_get_buffer(blkid_probe pr, uint64_t off, uint64_t len)
{
struct blkid_bufinfo *bf = NULL;
- uint64_t real_off = pr->off + off;
+ uint64_t real_off, bias, len_align;
+
+ bias = off % pr->io_size;
+ off -= bias;
+ len += bias;
+
+ if (len % pr->io_size) {
+ len_align = pr->io_size - (len % pr->io_size);
+
+ if (pr->off + off + len + len_align <= pr->size)
+ len += len_align;
+ }
+
+ real_off = pr->off + off;
/*
DBG(BUFFER, ul_debug("\t>>>> off=%ju, real-off=%ju (probe <%ju..%ju>, len=%ju",
off, real_off, pr->off, pr->off + pr->size, len));
*/
- if (pr->size == 0) {
+ if (pr->size == 0 || pr->io_size == 0) {
errno = EINVAL;
return NULL;
}
@@ -688,6 +759,11 @@ unsigned char *blkid_probe_get_buffer(blkid_probe pr, uint64_t off, uint64_t len
return NULL;
}
+ if (len > 8388608 /* 8 Mib */ ) {
+ DBG(BUFFER, ul_debug("\t too large read request (ignore)"));
+ return NULL;
+ }
+
if (len == 0
|| (!S_ISCHR(pr->mode) && (pr->size < off || pr->size < len))
|| (!S_ISCHR(pr->mode) && (pr->off + pr->size < real_off + len))) {
@@ -718,6 +794,7 @@ unsigned char *blkid_probe_get_buffer(blkid_probe pr, uint64_t off, uint64_t len
if (!bf)
return NULL;
+ mark_prunable_buffers(pr, bf);
list_add_tail(&bf->bufs, &pr->buffers);
}
@@ -725,7 +802,7 @@ unsigned char *blkid_probe_get_buffer(blkid_probe pr, uint64_t off, uint64_t len
assert(bf->off + bf->len >= real_off + len);
errno = 0;
- return real_off ? bf->data + (real_off - bf->off) : bf->data;
+ return real_off ? bf->data + (real_off - bf->off + bias) : bf->data + bias;
}
/**
@@ -747,6 +824,8 @@ int blkid_probe_reset_buffers(blkid_probe pr)
pr->flags &= ~BLKID_FL_MODIF_BUFF;
+ blkid_probe_prune_buffers(pr);
+
if (list_empty(&pr->buffers))
return 0;
@@ -757,11 +836,8 @@ int blkid_probe_reset_buffers(blkid_probe pr)
struct blkid_bufinfo, bufs);
ct++;
len += bf->len;
- list_del(&bf->bufs);
- DBG(BUFFER, ul_debug(" remove buffer: [off=%"PRIu64", len=%"PRIu64"]",
- bf->off, bf->len));
- free(bf);
+ remove_buffer(bf);
}
DBG(LOWPROBE, ul_debug(" buffers summary: %"PRIu64" bytes by %"PRIu64" read() calls",
@@ -891,6 +967,24 @@ failed:
#endif
+#ifdef BLKIOOPT
+static uint64_t blkid_get_io_size(int fd)
+{
+ static const int ioctls[] = { BLKIOOPT, BLKIOMIN, BLKBSZGET };
+ unsigned int s;
+ size_t i;
+ int r;
+
+ for (i = 0; i < ARRAY_SIZE(ioctls); i++) {
+ r = ioctl(fd, ioctls[i], &s);
+ if (r == 0 && is_power_of_2(s) && s >= DEFAULT_SECTOR_SIZE)
+ return min(s, 1U << 16);
+ }
+
+ return DEFAULT_SECTOR_SIZE;
+}
+#endif
+
/**
* blkid_probe_set_device:
* @pr: probe
@@ -934,6 +1028,7 @@ int blkid_probe_set_device(blkid_probe pr, int fd,
pr->fd = fd;
pr->off = (uint64_t) off;
pr->size = 0;
+ pr->io_size = DEFAULT_SECTOR_SIZE;
pr->devno = 0;
pr->disk_devno = 0;
pr->mode = 0;
@@ -1097,8 +1192,13 @@ int blkid_probe_set_device(blkid_probe pr, int fd,
}
# endif
- DBG(LOWPROBE, ul_debug("ready for low-probing, offset=%"PRIu64", size=%"PRIu64", zonesize=%"PRIu64,
- pr->off, pr->size, pr->zone_size));
+#ifdef BLKIOOPT
+ if (S_ISBLK(sb.st_mode) && !is_floppy && !blkid_probe_is_tiny(pr))
+ pr->io_size = blkid_get_io_size(fd);
+#endif
+
+ DBG(LOWPROBE, ul_debug("ready for low-probing, offset=%"PRIu64", size=%"PRIu64", zonesize=%"PRIu64", iosize=%"PRIu64,
+ pr->off, pr->size, pr->zone_size, pr->io_size));
DBG(LOWPROBE, ul_debug("whole-disk: %s, regfile: %s",
blkid_probe_is_wholedisk(pr) ?"YES" : "NO",
S_ISREG(pr->mode) ? "YES" : "NO"));
@@ -1136,14 +1236,28 @@ int blkid_probe_set_dimension(blkid_probe pr, uint64_t off, uint64_t size)
return 0;
}
-unsigned char *blkid_probe_get_sb_buffer(blkid_probe pr, const struct blkid_idmag *mag, size_t size)
+const unsigned char *blkid_probe_get_sb_buffer(blkid_probe pr, const struct blkid_idmag *mag, size_t size)
{
- uint64_t hint_offset;
+ uint64_t hint_offset, off;
+
+ if (mag->kboff >= 0) {
+ if (!mag->hoff || blkid_probe_get_hint(pr, mag->hoff, &hint_offset) < 0)
+ hint_offset = 0;
- if (!mag->hoff || blkid_probe_get_hint(pr, mag->hoff, &hint_offset) < 0)
- hint_offset = 0;
+ off = hint_offset + (mag->kboff << 10);
+ } else {
+ off = pr->size - (-mag->kboff << 10);
+ }
+
+ return blkid_probe_get_buffer(pr, off, size);
+}
- return blkid_probe_get_buffer(pr, hint_offset + (mag->kboff << 10), size);
+uint64_t blkid_probe_get_idmag_off(blkid_probe pr, const struct blkid_idmag *mag)
+{
+ if (mag->kboff >= 0)
+ return mag->kboff << 10;
+ else
+ return pr->size - (-mag->kboff << 10);
}
/*
@@ -1164,8 +1278,8 @@ int blkid_probe_get_idmag(blkid_probe pr, const struct blkid_idinfo *id,
/* try to detect by magic string */
while(mag && mag->magic) {
- unsigned char *buf;
- uint64_t kboff;
+ const unsigned char *buf;
+ long kboff;
uint64_t hint_offset;
if (!mag->hoff || blkid_probe_get_hint(pr, mag->hoff, &hint_offset) < 0)
@@ -1182,19 +1296,20 @@ int blkid_probe_get_idmag(blkid_probe pr, const struct blkid_idinfo *id,
else
kboff = ((mag->zonenum * pr->zone_size) >> 10) + mag->kboff_inzone;
- off = hint_offset + ((kboff + (mag->sboff >> 10)) << 10);
- buf = blkid_probe_get_buffer(pr, off, 1024);
+ if (kboff >= 0)
+ off = hint_offset + (kboff << 10) + mag->sboff;
+ else
+ off = pr->size - (-kboff << 10) + mag->sboff;
+ buf = blkid_probe_get_buffer(pr, off, mag->len);
if (!buf && errno)
return -errno;
- if (buf && !memcmp(mag->magic,
- buf + (mag->sboff & 0x3ff), mag->len)) {
-
- DBG(LOWPROBE, ul_debug("\tmagic sboff=%u, kboff=%" PRIu64,
+ if (buf && !memcmp(mag->magic, buf, mag->len)) {
+ DBG(LOWPROBE, ul_debug("\tmagic sboff=%u, kboff=%ld",
mag->sboff, kboff));
if (offset)
- *offset = off + (mag->sboff & 0x3ff);
+ *offset = off;
if (res)
*res = mag;
return BLKID_PROBE_OK;
@@ -1384,6 +1499,8 @@ static inline int is_conventional(blkid_probe pr __attribute__((__unused__)),
* See also blkid_probe_step_back() if you cannot use this built-in wipe
* function, but you want to use libblkid probing as a source for wiping.
*
+ * See also blkid_wipe_all() which works the same as the example above.
+ *
* Returns: 0 on success, and -1 in case of error.
*/
int blkid_do_wipe(blkid_probe pr, int dryrun)
@@ -1485,6 +1602,46 @@ int blkid_do_wipe(blkid_probe pr, int dryrun)
}
/**
+ * blkid_wipe_all:
+ * @pr: prober
+ *
+ * This function erases all detectable signatures from &pr.
+ * The @pr has to be open in O_RDWR mode. All other necessary configurations
+ * will be enabled automatically.
+ *
+ * <example>
+ * <title>wipe all filesystems or raids from the device</title>
+ * <programlisting>
+ * fd = open(devname, O_RDWR|O_CLOEXEC);
+ * blkid_probe_set_device(pr, fd, 0, 0);
+ *
+ * blkid_wipe_all(pr);
+ * </programlisting>
+ * </example>
+ *
+ * Returns: 0 on success, and -1 in case of error.
+ */
+int blkid_wipe_all(blkid_probe pr)
+{
+ DBG(LOWPROBE, ul_debug("wiping all signatures"));
+
+ blkid_probe_enable_superblocks(pr, 1);
+ blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_MAGIC |
+ BLKID_SUBLKS_BADCSUM);
+
+ blkid_probe_enable_partitions(pr, 1);
+ blkid_probe_set_partitions_flags(pr, BLKID_PARTS_MAGIC |
+ BLKID_PARTS_FORCE_GPT);
+
+ while (blkid_do_probe(pr) == 0) {
+ DBG(LOWPROBE, ul_debug("wiping one signature"));
+ blkid_do_wipe(pr, 0);
+ }
+
+ return BLKID_PROBE_OK;
+}
+
+/**
* blkid_probe_step_back:
* @pr: prober
*
@@ -1690,7 +1847,7 @@ done:
}
/* same sa blkid_probe_get_buffer() but works with 512-sectors */
-unsigned char *blkid_probe_get_sector(blkid_probe pr, unsigned int sector)
+const unsigned char *blkid_probe_get_sector(blkid_probe pr, unsigned int sector)
{
return blkid_probe_get_buffer(pr, ((uint64_t) sector) << 9, 0x200);
}