summaryrefslogtreecommitdiffstats
path: root/shlibs/blkid/src/probe.c
diff options
context:
space:
mode:
authorKarel Zak2010-01-18 15:43:25 +0100
committerKarel Zak2010-01-18 15:43:25 +0100
commit1ca17f911a4a039941185077b285378129eafb09 (patch)
tree2d9466557bfb06a7d6a30086cc6dd5c3f8497c29 /shlibs/blkid/src/probe.c
parentlibblkid: restrict RAID/FS proving for small devices (1.4MiB) (diff)
downloadkernel-qcow2-util-linux-1ca17f911a4a039941185077b285378129eafb09.tar.gz
kernel-qcow2-util-linux-1ca17f911a4a039941185077b285378129eafb09.tar.xz
kernel-qcow2-util-linux-1ca17f911a4a039941185077b285378129eafb09.zip
libblkid: read() optimization for small devices
- don't read the begin (69kB) of the device by one large read() - fill in the SB buffer dynamically - use extra buffer for FATs root dir entries (FAT FS label) on small devices to avoid large reads Detect FAT12 on 1.4MB device (number of bytes): Old version: $ strace -e read blkid -p floppy.img 2>&1 | \ awk -F ' = ' 'BEGIN {x=0} /read/ && !/.*ELF/ {x += $2} END { print x }' 73292 New version: $ strace -e read blkid -p floppy.img 2>&1 | \ awk -F ' = ' 'BEGIN {x=0} /read/ && !/.*ELF/ {x += $2} END { print x }' 8192 Reported-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Karel Zak <kzak@redhat.com>
Diffstat (limited to 'shlibs/blkid/src/probe.c')
-rw-r--r--shlibs/blkid/src/probe.c149
1 files changed, 99 insertions, 50 deletions
diff --git a/shlibs/blkid/src/probe.c b/shlibs/blkid/src/probe.c
index 6aad7ab45..2d3cafec4 100644
--- a/shlibs/blkid/src/probe.c
+++ b/shlibs/blkid/src/probe.c
@@ -470,6 +470,102 @@ int __blkid_probe_filter_types(blkid_probe pr, int chain, int flag, char *names[
return 0;
}
+static int blkid_probe_has_buffer(blkid_probe pr,
+ blkid_loff_t off, blkid_loff_t len)
+{
+ return pr && (off + len <= pr->sbbuf_len ||
+ (pr->buf_off < off && off + len < pr->buf_len));
+}
+
+/*
+ * Returns buffer from the begin (69kB) of the device.
+ */
+static unsigned char *blkid_probe_get_sb_buffer(blkid_probe pr,
+ blkid_loff_t off, blkid_loff_t len)
+{
+ if (off + len > BLKID_SB_BUFSIZ)
+ return NULL;
+ if (!pr->sbbuf) {
+ pr->sbbuf = malloc(BLKID_SB_BUFSIZ);
+ if (!pr->sbbuf)
+ return NULL;
+ }
+ if (off + len > pr->sbbuf_len) {
+ /*
+ * The sbbuf is not completely in memory.
+ *
+ * We don't read whole BLKID_SB_BUFSIZ by one read(), it's too
+ * aggresive to small devices (floppies). We read necessary
+ * data to complete the current request (off + len) only.
+ */
+ ssize_t ret_read;
+
+ blkid_loff_t have = pr->sbbuf_len,
+ want = off + len - have;
+
+ DBG(DEBUG_LOWPROBE,
+ printf("\tsb-buffer read() off=%jd len=%jd\n", have, want));
+
+ if (lseek(pr->fd, pr->off + have, SEEK_SET) < 0)
+ return NULL;
+
+ ret_read = read(pr->fd, pr->sbbuf + have, want);
+ if (ret_read < 0)
+ ret_read = 0;
+ pr->sbbuf_len = have + ret_read;
+ }
+ if (off + len > pr->sbbuf_len)
+ return NULL;
+ return pr->sbbuf + off;
+}
+
+/*
+ * Returns pointer to the buffer on arbitrary offset on the device
+ */
+unsigned char *blkid_probe_get_extra_buffer(blkid_probe pr,
+ blkid_loff_t off, blkid_loff_t len)
+{
+ unsigned char *newbuf = NULL;
+
+ if (off + len <= BLKID_SB_BUFSIZ &&
+ (!blkid_probe_is_tiny(pr) || blkid_probe_has_buffer(pr, off, len)))
+ /*
+ * Don't use extra buffer for superblock data if
+ * - data are already in SB buffer
+ * - or the device is large and we needn't extra
+ * optimalization for tiny devices
+ */
+ return blkid_probe_get_sb_buffer(pr, off, len);
+
+ if (len > pr->buf_max) {
+ newbuf = realloc(pr->buf, len);
+ if (!newbuf)
+ return NULL;
+ pr->buf = newbuf;
+ pr->buf_max = len;
+ pr->buf_off = 0;
+ pr->buf_len = 0;
+ }
+ if (newbuf || off < pr->buf_off ||
+ off + len > pr->buf_off + pr->buf_len) {
+ ssize_t ret_read;
+
+ if (blkid_llseek(pr->fd, pr->off + off, SEEK_SET) < 0)
+ return NULL;
+
+ DBG(DEBUG_LOWPROBE,
+ printf("\textra-buffer read: off=%jd len=%jd\n", off, len));
+
+ ret_read = read(pr->fd, pr->buf, len);
+ if (ret_read != (ssize_t) len)
+ return NULL;
+ pr->buf_off = off;
+ pr->buf_len = len;
+ }
+ return off ? pr->buf + (off - pr->buf_off) : pr->buf;
+}
+
+
/*
* @off: offset within probing area
* @len: size of requested buffer
@@ -493,62 +589,16 @@ int __blkid_probe_filter_types(blkid_probe pr, int chain, int flag, char *names[
unsigned char *blkid_probe_get_buffer(blkid_probe pr,
blkid_loff_t off, blkid_loff_t len)
{
- ssize_t ret_read = 0;
-
if (off < 0 || len < 0) {
DBG(DEBUG_LOWPROBE,
printf("unexpected offset or length of buffer requested\n"));
return NULL;
}
-
if (off + len > pr->size)
return NULL;
-
- DBG(DEBUG_LOWPROBE,
- printf("\tbuffer: offset=%jd size=%jd\n", off, len));
-
- if (off + len <= BLKID_SB_BUFSIZ) {
- if (!pr->sbbuf) {
- pr->sbbuf = malloc(BLKID_SB_BUFSIZ);
- if (!pr->sbbuf)
- return NULL;
- }
- if (!pr->sbbuf_len) {
- if (lseek(pr->fd, pr->off, SEEK_SET) < 0)
- return NULL;
- ret_read = read(pr->fd, pr->sbbuf, BLKID_SB_BUFSIZ);
- if (ret_read < 0)
- ret_read = 0;
- pr->sbbuf_len = ret_read;
- }
- if (off + len > pr->sbbuf_len)
- return NULL;
- return pr->sbbuf + off;
- } else {
- unsigned char *newbuf = NULL;
-
- if (len > pr->buf_max) {
- newbuf = realloc(pr->buf, len);
- if (!newbuf)
- return NULL;
- pr->buf = newbuf;
- pr->buf_max = len;
- pr->buf_off = 0;
- pr->buf_len = 0;
- }
- if (newbuf || off < pr->buf_off ||
- off + len > pr->buf_off + pr->buf_len) {
- if (blkid_llseek(pr->fd, pr->off + off, SEEK_SET) < 0)
- return NULL;
-
- ret_read = read(pr->fd, pr->buf, len);
- if (ret_read != (ssize_t) len)
- return NULL;
- pr->buf_off = off;
- pr->buf_len = len;
- }
- return off ? pr->buf + (off - pr->buf_off) : pr->buf;
- }
+ if (off + len <= BLKID_SB_BUFSIZ)
+ return blkid_probe_get_sb_buffer(pr, off, len);
+ return blkid_probe_get_extra_buffer(pr, off, len);
}
/*
@@ -620,7 +670,6 @@ int blkid_probe_set_device(blkid_probe pr, int fd,
if (!pr->size)
goto err;
-
DBG(DEBUG_LOWPROBE, printf("ready for low-probing, offset=%zd, size=%zd\n",
pr->off, pr->size));
return 0;