[4/5] locks: Add leases c/r for old kernels (<= v4.0)

Submitted by Pavel Begunkov (Silence) on Aug. 13, 2017, 10:27 p.m.

Details

Message ID 20170813222709.13911-4-asml.silence@gmail.com
State Accepted
Series "Series without cover letter"
Headers show

Commit Message

Pavel Begunkov (Silence) Aug. 13, 2017, 10:27 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.

task #39

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

Patch hide | download patch | download mbox

diff --git a/criu/file-lock.c b/criu/file-lock.c
index e13834f64..584b6b627 100644
--- a/criu/file-lock.c
+++ b/criu/file-lock.c
@@ -266,6 +266,81 @@  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. Otherwise it
+		 * will fail because of breaking sequence.
+		 */
+		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;
+		} else {
+			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;
@@ -295,8 +370,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;