[2/5] locks: Add c/r of breaking leases (kernel >=v4.1)

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

Details

Message ID 20170813222709.13911-2-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.
Because leases can be taken out only on regular files, it gets file path
and trying to open the file with it in order to break the lease.

task #39

Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
---
 criu/file-lock.c         | 89 +++++++++++++++++++++++++++++++++++++++++++++++-
 criu/files.c             |  1 +
 criu/include/file-lock.h |  3 ++
 criu/proc_parse.c        |  7 ++--
 4 files changed, 96 insertions(+), 4 deletions(-)

Patch hide | download patch | download mbox

diff --git a/criu/file-lock.c b/criu/file-lock.c
index 92d8bd394..496eac74d 100644
--- a/criu/file-lock.c
+++ b/criu/file-lock.c
@@ -18,6 +18,8 @@ 
 #include "proc_parse.h"
 #include "servicefd.h"
 #include "file-lock.h"
+#include "pstree.h"
+#include "files-reg.h"
 
 struct file_lock_rst {
 	FileLockEntry *fle;
@@ -332,6 +334,91 @@  int note_file_lock(struct pid *pid, int fd, int lfd, struct fd_parms *p)
 	return 0;
 }
 
+static int open_break_cb(int ns_root_fd, struct reg_file_info *rfi, void *arg)
+{
+	int fd, flags = *(int *)arg | O_NONBLOCK;
+
+	fd = openat(ns_root_fd, rfi->path, flags);
+	if (fd >= 0) {
+		pr_err("Conflicting lease wasn't found\n");
+		close(fd);
+		return -1;
+	}
+	if (errno != EWOULDBLOCK) {
+		pr_perror("Can't open file to break lease\n");
+		return -1;
+	}
+	return 0;
+}
+
+static int set_lease_for_breaking(int fd, int fd_type)
+{
+	int lease_type;
+
+	if (fd_type == O_RDONLY) {
+		lease_type = F_RDLCK;
+	} else if (fd_type == O_WRONLY) {
+		lease_type = F_WRLCK;
+	} else {
+		pr_err("Incompatible with lease file type (%i)\n", fd_type);
+		return -1;
+	}
+
+	if (fcntl(fd, F_SETLEASE, lease_type) < 0) {
+		pr_perror("Can't set lease to fd %#x\n", fd);
+		return -1;
+	}
+	return 0;
+}
+
+static struct fdinfo_list_entry *find_fd(struct pstree_item *task, int fd)
+{
+	struct list_head *head = &rsti(task)->fds;
+	struct fdinfo_list_entry *fle;
+
+	list_for_each_entry_reverse(fle, head, ps_list) {
+		if (fle->fe->fd == fd)
+			return fle;
+	}
+	return NULL;
+}
+
+static int restore_breaking_file_lease(FileLockEntry *fle)
+{
+	struct fdinfo_list_entry *fdle;
+	int break_flags;
+	int lease_break_type = fle->type & (~LEASE_BREAKING);
+
+	fdle = find_fd(current, fle->fd);
+	if (fdle == NULL) {
+		pr_err("Can't get file description\n");
+		return -1;
+	}
+
+	if (set_lease_for_breaking(fle->fd, fdle->desc->ops->type))
+		return -1;
+
+	if (lease_break_type == F_UNLCK) {
+		break_flags = O_WRONLY;
+	} else if (lease_break_type == F_RDLCK) {
+		break_flags = O_RDONLY;
+	} else {
+		pr_err("Incorrect breaking type of lease\n");
+		return -1;
+	}
+	if (open_path(fdle->desc, open_break_cb, (void *)&break_flags) < 0)
+		return -1;
+	return 0;
+}
+
+static int restore_file_lease(FileLockEntry *fle)
+{
+	if (fle->type & LEASE_BREAKING)
+		return restore_breaking_file_lease(fle);
+	else
+		return fcntl(fle->fd, F_SETLEASE, fle->type);
+}
+
 static int restore_file_lock(FileLockEntry *fle)
 {
 	int ret = -1;
@@ -399,7 +486,7 @@  static int restore_file_lock(FileLockEntry *fle)
 			goto err;
 		}
 	} else if (fle->flag & FL_LEASE) {
-		ret = fcntl(fle->fd, F_SETLEASE, fle->type);
+		ret = restore_file_lease(fle);
 		if (ret < 0) {
 			pr_perror("Can't set lease!\n");
 			goto err;
diff --git a/criu/files.c b/criu/files.c
index 01cd4c0e9..2c3389e3f 100644
--- a/criu/files.c
+++ b/criu/files.c
@@ -14,6 +14,7 @@ 
 #include <stdlib.h>
 
 #include "types.h"
+#include "kerndat.h"
 #include "files.h"
 #include "file-ids.h"
 #include "files-reg.h"
diff --git a/criu/include/file-lock.h b/criu/include/file-lock.h
index f70739adb..7280e0b55 100644
--- a/criu/include/file-lock.h
+++ b/criu/include/file-lock.h
@@ -38,6 +38,9 @@ 
 #define LOCK_WRITE	128	/* which allows concurrent write operations */
 #define LOCK_RW		192	/* which allows concurrent read & write ops */
 
+/* for leases */
+#define LEASE_BREAKING	4
+
 struct file_lock {
 	long long	fl_id;
 	int		fl_kind;
diff --git a/criu/proc_parse.c b/criu/proc_parse.c
index d3893272c..89be4ceb5 100644
--- a/criu/proc_parse.c
+++ b/criu/proc_parse.c
@@ -1996,6 +1996,10 @@  static int parse_file_lock_buf(char *buf, struct file_lock *fl,
 	else
 		fl->fl_kind = FL_UNKNOWN;
 
+	if (fl->fl_kind == FL_LEASE && !strcmp(fl_type, "BREAKING")) {
+		fl->fl_ltype |= LEASE_BREAKING;
+	}
+
 	if (!strcmp(fl_type, "MSNFS")) {
 		fl->fl_ltype |= LOCK_MAND;
 
@@ -2009,9 +2013,6 @@  static int parse_file_lock_buf(char *buf, struct file_lock *fl,
 			pr_err("Unknown lock option!\n");
 			return -1;
 		}
-	} else if (fl->fl_kind == FL_LEASE && !strcmp(fl_type, "BREAKING")) {
-		pr_err("Breaking leases are not supported (%d): %s\n",
-			num, buf);
 	} else {
 		if (!strcmp(fl_option, "UNLCK")) {
 			fl->fl_ltype |= F_UNLCK;