summaryrefslogtreecommitdiffstats
path: root/lib/libstand/lseek.c
diff options
context:
space:
mode:
authorjdp <jdp@FreeBSD.org>2001-08-29 23:33:22 +0000
committerjdp <jdp@FreeBSD.org>2001-08-29 23:33:22 +0000
commitd118ae3895cbb757e23c8a19dfe269d7e5348b2f (patch)
treef7d7611e2a30c11315342a7726786b5d219842e1 /lib/libstand/lseek.c
parentbf5195f527c9da4fa106d450b90fe51082bd020d (diff)
downloadFreeBSD-src-d118ae3895cbb757e23c8a19dfe269d7e5348b2f.zip
FreeBSD-src-d118ae3895cbb757e23c8a19dfe269d7e5348b2f.tar.gz
Fix a bug in lseek which caused the loader to fail on some gzipped
kernels. The error message was "elf_loadexec: cannot seek". Libstand maintains a read-ahead buffer for each open file, so that it can read in chunks of 512 bytes for greater efficiency. When the loader tries to lseek forward in a file by a small amount, it sometimes happens that the target file offset is already in the read-ahead buffer. But the lseek code simply discarded the contents of that buffer and performed a seek directly on the underlying file. This resulted in an attempt to seek backwards in the file, since some of the data has already been read into the read-ahead buffer. Gzipped data streams cannot seek backwards, so an error was returned. This commit adds code which checks to see if the desired file offset is already in the read-ahead buffer. If it is, the code simply adjusts the buffer pointer and length, thereby avoiding a reverse seek on the gzipped data stream. I incorporated a suggestion from Matt Dillon which saved a little bit of code in this fix. Reviewed by: dillon, gallatin, jhb
Diffstat (limited to 'lib/libstand/lseek.c')
-rw-r--r--lib/libstand/lseek.c31
1 files changed, 31 insertions, 0 deletions
diff --git a/lib/libstand/lseek.c b/lib/libstand/lseek.c
index 91f8612..f945460 100644
--- a/lib/libstand/lseek.c
+++ b/lib/libstand/lseek.c
@@ -70,6 +70,7 @@
off_t
lseek(int fd, off_t offset, int where)
{
+ off_t bufpos, filepos, target;
struct open_file *f = &files[fd];
if ((unsigned)fd >= SOPEN_MAX || f->f_flags == 0) {
@@ -97,6 +98,36 @@ lseek(int fd, off_t offset, int where)
}
/*
+ * If there is some unconsumed data in the readahead buffer and it
+ * contains the desired offset, simply adjust the buffer offset and
+ * length. We don't bother with SEEK_END here, since the code to
+ * handle it would fail in the same cases where the non-readahead
+ * code fails (namely, for streams which cannot seek backward and whose
+ * size isn't known in advance).
+ */
+ if (f->f_ralen != 0 && where != SEEK_END) {
+ if ((filepos = (f->f_ops->fo_seek)(f, (off_t)0, SEEK_CUR)) == -1)
+ return (-1);
+ bufpos = filepos - f->f_ralen;
+ switch (where) {
+ case SEEK_SET:
+ target = offset;
+ break;
+ case SEEK_CUR:
+ target = bufpos + offset;
+ break;
+ default:
+ errno = EINVAL;
+ return (-1);
+ }
+ if (bufpos <= target && target < filepos) {
+ f->f_raoffset += target - bufpos;
+ f->f_ralen -= target - bufpos;
+ return (target);
+ }
+ }
+
+ /*
* If this is a relative seek, we need to correct the offset for
* bytes that we have already read but the caller doesn't know
* about.
OpenPOWER on IntegriCloud