+/* Zero length means "to end of file" */
+static off_t end_of(off_t start, off_t len)
+{
+ if (len == 0)
+ return off_max();
+ return start + len - 1;
+}
+
+/* FIXME: This only handles locks, really. */
+int failtest_fcntl(int fd, const char *file, unsigned line, int cmd, ...)
+{
+ struct failtest_call *p;
+ struct fcntl_call call;
+ va_list ap;
+
+ call.fd = fd;
+ call.cmd = cmd;
+
+ /* Argument extraction. */
+ switch (cmd) {
+ case F_SETFL:
+ case F_SETFD:
+ va_start(ap, cmd);
+ call.arg.l = va_arg(ap, long);
+ va_end(ap);
+ return fcntl(fd, cmd, call.arg.l);
+ case F_GETFD:
+ case F_GETFL:
+ return fcntl(fd, cmd);
+ case F_GETLK:
+ get_locks();
+ va_start(ap, cmd);
+ call.arg.fl = *va_arg(ap, struct flock *);
+ va_end(ap);
+ return fcntl(fd, cmd, &call.arg.fl);
+ case F_SETLK:
+ case F_SETLKW:
+ va_start(ap, cmd);
+ call.arg.fl = *va_arg(ap, struct flock *);
+ va_end(ap);
+ break;
+ default:
+ /* This means you need to implement it here. */
+ err(1, "failtest: unknown fcntl %u", cmd);
+ }
+
+ p = add_history(FAILTEST_FCNTL, file, line, &call);
+ get_locks();
+
+ if (should_fail(p)) {
+ p->u.fcntl.ret = -1;
+ if (p->u.fcntl.cmd == F_SETLK)
+ p->error = EAGAIN;
+ else
+ p->error = EDEADLK;
+ } else {
+ p->u.fcntl.ret = fcntl(p->u.fcntl.fd, p->u.fcntl.cmd,
+ &p->u.fcntl.arg.fl);
+ if (p->u.fcntl.ret == -1)
+ p->error = errno;
+ else {
+ /* We don't handle anything else yet. */
+ assert(p->u.fcntl.arg.fl.l_whence == SEEK_SET);
+ locks = add_lock(locks,
+ p->u.fcntl.fd,
+ p->u.fcntl.arg.fl.l_start,
+ end_of(p->u.fcntl.arg.fl.l_start,
+ p->u.fcntl.arg.fl.l_len),
+ p->u.fcntl.arg.fl.l_type);
+ }
+ }
+ errno = p->error;
+ return p->u.fcntl.ret;
+}
+