+ssize_t failtest_read(int fd, void *buf, size_t count,
+ const char *file, unsigned line)
+{
+ return failtest_pread(fd, buf, count, lseek(fd, 0, SEEK_CUR),
+ file, line);
+}
+
+ssize_t failtest_write(int fd, const void *buf, size_t count,
+ const char *file, unsigned line)
+{
+ return failtest_pwrite(fd, buf, count, lseek(fd, 0, SEEK_CUR),
+ file, line);
+}
+
+static struct lock_info *WARN_UNUSED_RESULT
+add_lock(struct lock_info *locks, int fd, off_t start, off_t end, int type)
+{
+ unsigned int i;
+ struct lock_info *l;
+
+ for (i = 0; i < lock_num; i++) {
+ l = &locks[i];
+
+ if (l->fd != fd)
+ continue;
+ /* Four cases we care about:
+ * Start overlap:
+ * l = | |
+ * new = | |
+ * Mid overlap:
+ * l = | |
+ * new = | |
+ * End overlap:
+ * l = | |
+ * new = | |
+ * Total overlap:
+ * l = | |
+ * new = | |
+ */
+ if (start > l->start && end < l->end) {
+ /* Mid overlap: trim entry, add new one. */
+ off_t new_start, new_end;
+ new_start = end + 1;
+ new_end = l->end;
+ l->end = start - 1;
+ locks = add_lock(locks,
+ fd, new_start, new_end, l->type);
+ l = &locks[i];
+ } else if (start <= l->start && end >= l->end) {
+ /* Total overlap: eliminate entry. */
+ l->end = 0;
+ l->start = 1;
+ } else if (end >= l->start && end < l->end) {
+ /* Start overlap: trim entry. */
+ l->start = end + 1;
+ } else if (start > l->start && start <= l->end) {
+ /* End overlap: trim entry. */
+ l->end = start-1;
+ }
+ /* Nothing left? Remove it. */
+ if (l->end < l->start) {
+ memmove(l, l + 1, (--lock_num - i) * sizeof(l[0]));
+ i--;
+ }
+ }
+
+ if (type != F_UNLCK) {
+ locks = realloc(locks, (lock_num + 1) * sizeof(*locks));
+ l = &locks[lock_num++];
+ l->fd = fd;
+ l->start = start;
+ l->end = end;
+ l->type = type;
+ }
+ return locks;
+}
+