[v4,3/4] locks: Add leases c/r for kernels v4.0 and older

Submitted by Pavel Begunkov on Sept. 9, 2017, 4:51 p.m.

Details

Message ID 20170909165140.30434-4-asml.silence@gmail.com
State New
Series "file leases support"
Headers show

Commit Message

Pavel Begunkov Sept. 9, 2017, 4:51 p.m.
Information about locks in /proc/<pid>/fdinfo is presented only since
kernel v4.1. This patch adds logic to *note_file_lock* to match leases
and OFDs.

Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
---
 criu/file-lock.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 82 insertions(+), 2 deletions(-)

Patch hide | download patch | download mbox

diff --git a/criu/file-lock.c b/criu/file-lock.c
index 7fc1ef5c7..f776c98e4 100644
--- a/criu/file-lock.c
+++ b/criu/file-lock.c
@@ -269,6 +269,80 @@  static int lock_ofd_check_fd(int lfd, struct file_lock *fl)
 	return 1;
 }
 
+static int lease_check_fd(int fd, int file_flags, struct file_lock *fl)
+{
+	int file_lease_type, err;
+	int lease_type = fl->fl_ltype & (~LEASE_BREAKING);
+
+	if ((file_flags & O_ACCMODE) != O_RDONLY) {
+		/*
+		 * Write OFD conflicts with any lease not associated
+		 * with it, therefore there is can't be other lease
+		 * or OFD for this file.
+		 */
+		return 1;
+	}
+
+	file_lease_type = fcntl(fd, F_GETLEASE);
+	if (file_lease_type < 0) {
+		pr_err("Can't get lease type\n");
+		return -1;
+	}
+
+	/*
+	 * Only read OFDs can be present for the file. If
+	 * read and write OFDs with at least one lease had
+	 * presented, it would have conflicted.
+	 */
+	if (fl->fl_ltype & LEASE_BREAKING) {
+		/*
+		 * Only read leases are possible for read OFDs
+		 * and they all should be in breaking state,
+		 * because the current one is.
+		 */
+		int compatible_type = file_lease_type;
+
+		if (compatible_type != F_UNLCK) {
+			pr_err("Lease doesn't conflicts but breaks\n");
+			return -1;
+		}
+		/*
+		 * Due to activated breaking sequence we can't
+		 * get actual lease type with F_GETLEASE.
+		 * The err == 0 after lease upgrade means, that
+		 * there is already read lease on OFD. Otherwise
+		 * it would fail, because current read lease is
+		 * still set and breaking.
+		 */
+		err = fcntl(fd, F_SETLEASE, F_RDLCK);
+		if (err < 0) {
+			if (errno != EAGAIN) {
+				pr_perror("Can't set lease (fd %i)", fd);
+				return -1;
+			}
+			return 0;
+		}
+		return 1;
+	} else {
+		/*
+		 * The file can have only non-breaking read
+		 * leases, because otherwise the current one
+		 * also would have broke.
+		 */
+		if (lease_type != F_RDLCK) {
+			pr_err("Incorrect lease type\n");
+			return -1;
+		}
+
+		if (file_lease_type == F_UNLCK)
+			return 0;
+		if (file_lease_type == F_RDLCK)
+			return 1;
+		pr_err("Invalid file lease type\n");
+		return -1;
+	}
+}
+
 int note_file_lock(struct pid *pid, int fd, int lfd, struct fd_parms *p)
 {
 	struct file_lock *fl;
@@ -298,8 +372,14 @@  int note_file_lock(struct pid *pid, int fd, int lfd, struct fd_parms *p)
 			if (fl->fl_owner != pid->real)
 				continue;
 		} else if (fl->fl_kind == FL_LEASE) {
-			pr_err("Leases are not supported for kernel <= v4.0");
-			return -1;
+			if (fl->owners_fd >= 0)
+				continue;
+
+			ret = lease_check_fd(lfd, p->flags, fl);
+			if (ret < 0)
+				return ret;
+			if (ret == 0)
+				continue;
 		} else /* fl->fl_kind == FL_FLOCK || fl->fl_kind == FL_OFD */ {
 			int ret;