+static void cleanup_lseek(struct lseek_call *call, bool restore)
+{
+ if (restore) {
+ trace("cleaning up lseek on fd %i -> %llu\n",
+ call->fd, (long long)call->old_off);
+ if (lseek(call->fd, call->old_off, SEEK_SET) != call->old_off)
+ fwarn("Restoring lseek pointer failed");
+ }
+}
+
+/* We trap this so we can undo it: we don't fail it. */
+off_t failtest_lseek(int fd, off_t offset, int whence, const char *file,
+ unsigned int line)
+{
+ struct failtest_call *p;
+ struct lseek_call call;
+ call.fd = fd;
+ call.offset = offset;
+ call.whence = whence;
+ call.old_off = lseek(fd, 0, SEEK_CUR);
+
+ p = add_history(FAILTEST_LSEEK, false, file, line, &call);
+ p->fail = false;
+
+ /* Consume lseek from failpath. */
+ if (failpath)
+ if (should_fail(p))
+ abort();
+
+ p->u.lseek.ret = lseek(fd, offset, whence);
+
+ if (p->u.lseek.ret != (off_t)-1)
+ set_cleanup(p, cleanup_lseek, struct lseek_call);
+
+ trace("lseek %s:%u on fd %i from %llu to %llu%s\n",
+ file, line, fd, (long long)call.old_off, (long long)offset,
+ whence == SEEK_CUR ? " (from current off)" :
+ whence == SEEK_END ? " (from end)" :
+ whence == SEEK_SET ? "" : " (invalid whence)");
+ return p->u.lseek.ret;
+}
+
+