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

Submitted by Pavel Begunkov (Silence) on Sept. 7, 2017, 8:05 a.m.

Details

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

Commit Message

Pavel Begunkov (Silence) Sept. 7, 2017, 8:05 a.m.
Leases can be taken out only on regular files. In order to restore
breaking lease it break restored lease by opening a file with which
lease is associated.

Also, the patch fix type of broken leases to 'target lease type',
because procfs always returns 'READ' in this case.

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

Patch hide | download patch | download mbox

diff --git a/criu/file-lock.c b/criu/file-lock.c
index 92d8bd394..3f189a113 100644
--- a/criu/file-lock.c
+++ b/criu/file-lock.c
@@ -1,6 +1,7 @@ 
 #include <stdlib.h>
 #include <unistd.h>
 #include <sys/file.h>
+#include <sys/fsuid.h>
 #include <fcntl.h>
 #include <string.h>
 #include <sys/types.h>
@@ -18,6 +19,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 +335,164 @@  int note_file_lock(struct pid *pid, int fd, int lfd, struct fd_parms *p)
 	return 0;
 }
 
+int correct_file_leases_type(struct pid *pid, int fd, int lfd)
+{
+	struct file_lock *fl;
+	int target_type;
+
+	list_for_each_entry(fl, &file_lock_list, list) {
+		/* owners_fd should be set before usage */
+		if (fl->fl_owner != pid->real || fl->owners_fd != fd)
+			continue;
+
+		if (fl->fl_kind == FL_LEASE &&
+			(fl->fl_ltype & LEASE_BREAKING)) {
+			/*
+			 * Set lease type to actual 'target lease type'
+			 * instead of 'READ' returned by procfs.
+			 */
+			target_type = fcntl(lfd, F_GETLEASE);
+			if (target_type < 0) {
+				perror("Can't get lease type\n");
+				return -1;
+			}
+
+			fl->fl_ltype &= ~O_ACCMODE;
+			fl->fl_ltype |= target_type;
+			break;
+		}
+	}
+	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 break lease\n");
+		return -1;
+	}
+	return 0;
+}
+
+static int break_lease(int lease_type, struct file_desc *desc)
+{
+	int target_type = lease_type & (~LEASE_BREAKING);
+	int break_flags;
+
+	/*
+	 * Flags for open call chosen in a way to even
+	 * 'target lease type' returned by fcntl(F_GETLEASE)
+	 * and lease type from the image.
+	 */
+	if (target_type == F_UNLCK) {
+		break_flags = O_WRONLY;
+	} else if (target_type == F_RDLCK) {
+		break_flags = O_RDONLY;
+	} else {
+		pr_err("Incorrect target lease type\n");
+		return -1;
+	}
+	return open_path(desc, open_break_cb, (void *)&break_flags);
+}
+
+static int set_file_lease(int fd, int type)
+{
+	int old_fsuid, ret;
+	struct stat st;
+
+	if (fstat(fd, &st)) {
+		pr_perror("Can't get file stat (%i)\n", fd);
+		return -1;
+	}
+
+	/*
+	 * An unprivileged process may take out a lease only if
+	 * uid of the file matches the fsuid of the process.
+	 */
+	old_fsuid = setfsuid(st.st_uid);
+
+	ret = fcntl(fd, F_SETLEASE, type);
+	if (ret < 0)
+		pr_perror("Can't set lease\n");
+
+	setfsuid(old_fsuid);
+	return ret;
+}
+
+static int restore_lease_prebroken_state(int fd, int fd_type)
+{
+	int access_flags = fd_type & O_ACCMODE;
+	int lease_type = (access_flags == O_RDONLY) ? F_RDLCK : F_WRLCK;
+
+	if (set_file_lease(fd, lease_type) < 0) {
+		pr_perror("Can't set lease (fd %i)\n", fd);
+		return -1;
+	}
+	return 0;
+}
+
+static struct fdinfo_list_entry *find_fd_unordered(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_broken_file_lease(FileLockEntry *fle)
+{
+	struct fdinfo_list_entry *fdle;
+
+	fdle = find_fd_unordered(current, fle->fd);
+	if (fdle == NULL) {
+		pr_err("Can't get file description\n");
+		return -1;
+	}
+
+	if (restore_lease_prebroken_state(fle->fd, fdle->desc->ops->type))
+		return -1;
+
+	/*
+	 * It could be broken by 2 types of open call:
+	 * 1. non-blocking: It failed because of the lease.
+	 * 2. blocking: It had been blocked at the moment
+	 * of dumping, otherwise lease wouldn't be broken.
+	 * Thus, it was canceled by CRIU.
+	 *
+	 * There are no files or leases in image, which will
+	 * conflict with each other. Therefore we should explicitly
+	 * break leases. Restoring can be done in any order.
+	 */
+	return break_lease(fle->type, fdle->desc);
+}
+
+static int restore_file_lease(FileLockEntry *fle)
+{
+	int ret;
+
+	if (fle->type & LEASE_BREAKING) {
+		return restore_broken_file_lease(fle);
+	} else {
+		ret = set_file_lease(fle->fd, fle->type);
+		if (ret < 0)
+			pr_perror("Can't restore non broken lease");
+		return ret;
+	}
+}
+
 static int restore_file_lock(FileLockEntry *fle)
 {
 	int ret = -1;
@@ -399,11 +560,9 @@  static int restore_file_lock(FileLockEntry *fle)
 			goto err;
 		}
 	} else if (fle->flag & FL_LEASE) {
-		ret = fcntl(fle->fd, F_SETLEASE, fle->type);
-		if (ret < 0) {
-			pr_perror("Can't set lease!\n");
+		ret = restore_file_lease(fle);
+		if (ret < 0)
 			goto err;
-		}
 	} else {
 		pr_err("Unknown file lock style!\n");
 		goto err;
diff --git a/criu/files.c b/criu/files.c
index 01cd4c0e9..c103cbc1d 100644
--- a/criu/files.c
+++ b/criu/files.c
@@ -458,6 +458,9 @@  static int dump_one_file(struct pid *pid, int fd, int lfd, struct fd_opts *opts,
 	if (note_file_lock(pid, fd, lfd, &p))
 		return -1;
 
+	if (correct_file_leases_type(pid, fd, lfd))
+		return -1;
+
 	p.fd_ctl = ctl; /* Some dump_opts require this to talk to parasite */
 
 	if (S_ISSOCK(p.stat.st_mode))
diff --git a/criu/include/file-lock.h b/criu/include/file-lock.h
index f70739adb..7b19524d7 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;
@@ -65,6 +68,7 @@  extern struct collect_image_info file_locks_cinfo;
 
 struct pid;
 struct fd_parms;
+extern int correct_file_leases_type(struct pid *, int fd, int lfd);
 extern int note_file_lock(struct pid *, int fd, int lfd, struct fd_parms *);
 extern int dump_file_locks(void);
 
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;

Comments

Andrei Vagin Sept. 7, 2017, 5:06 p.m.
On Thu, Sep 07, 2017 at 11:05:27AM +0300, Pavel Begunkov wrote:
> Leases can be taken out only on regular files. In order to restore
> breaking lease it break restored lease by opening a file with which
> lease is associated.
> 
> Also, the patch fix type of broken leases to 'target lease type',
> because procfs always returns 'READ' in this case.
> 
> #39
> Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
> ---
>  criu/file-lock.c         | 167 +++++++++++++++++++++++++++++++++++++++++++++--
>  criu/files.c             |   3 +
>  criu/include/file-lock.h |   4 ++
>  criu/proc_parse.c        |   7 +-
>  4 files changed, 174 insertions(+), 7 deletions(-)
> 
> diff --git a/criu/file-lock.c b/criu/file-lock.c
> index 92d8bd394..3f189a113 100644
> --- a/criu/file-lock.c
> +++ b/criu/file-lock.c
> @@ -1,6 +1,7 @@
>  #include <stdlib.h>
>  #include <unistd.h>
>  #include <sys/file.h>
> +#include <sys/fsuid.h>
>  #include <fcntl.h>
>  #include <string.h>
>  #include <sys/types.h>
> @@ -18,6 +19,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 +335,164 @@ int note_file_lock(struct pid *pid, int fd, int lfd, struct fd_parms *p)
>  	return 0;
>  }
>  
> +int correct_file_leases_type(struct pid *pid, int fd, int lfd)
> +{
> +	struct file_lock *fl;
> +	int target_type;
> +
> +	list_for_each_entry(fl, &file_lock_list, list) {
> +		/* owners_fd should be set before usage */
> +		if (fl->fl_owner != pid->real || fl->owners_fd != fd)
> +			continue;
> +
> +		if (fl->fl_kind == FL_LEASE &&
> +			(fl->fl_ltype & LEASE_BREAKING)) {
> +			/*
> +			 * Set lease type to actual 'target lease type'
> +			 * instead of 'READ' returned by procfs.
> +			 */
> +			target_type = fcntl(lfd, F_GETLEASE);
> +			if (target_type < 0) {
> +				perror("Can't get lease type\n");
> +				return -1;
> +			}
> +
> +			fl->fl_ltype &= ~O_ACCMODE;
> +			fl->fl_ltype |= target_type;
> +			break;
> +		}
> +	}
> +	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 break lease\n");
> +		return -1;
> +	}
> +	return 0;
> +}
> +
> +static int break_lease(int lease_type, struct file_desc *desc)
> +{
> +	int target_type = lease_type & (~LEASE_BREAKING);
> +	int break_flags;
> +
> +	/*
> +	 * Flags for open call chosen in a way to even
> +	 * 'target lease type' returned by fcntl(F_GETLEASE)
> +	 * and lease type from the image.
> +	 */
> +	if (target_type == F_UNLCK) {
> +		break_flags = O_WRONLY;
> +	} else if (target_type == F_RDLCK) {
> +		break_flags = O_RDONLY;
> +	} else {
> +		pr_err("Incorrect target lease type\n");
> +		return -1;
> +	}
> +	return open_path(desc, open_break_cb, (void *)&break_flags);
> +}
> +
> +static int set_file_lease(int fd, int type)
> +{
> +	int old_fsuid, ret;
> +	struct stat st;
> +
> +	if (fstat(fd, &st)) {
> +		pr_perror("Can't get file stat (%i)\n", fd);
> +		return -1;
> +	}
> +
> +	/*
> +	 * An unprivileged process may take out a lease only if
> +	 * uid of the file matches the fsuid of the process.
> +	 */
> +	old_fsuid = setfsuid(st.st_uid);
> +
> +	ret = fcntl(fd, F_SETLEASE, type);
> +	if (ret < 0)
> +		pr_perror("Can't set lease\n");

This code has to be in a previous patch. It isn't about broken leases,
isn't it?

> +
> +	setfsuid(old_fsuid);
> +	return ret;
> +}
> +
> +static int restore_lease_prebroken_state(int fd, int fd_type)
> +{
> +	int access_flags = fd_type & O_ACCMODE;
> +	int lease_type = (access_flags == O_RDONLY) ? F_RDLCK : F_WRLCK;
> +
> +	if (set_file_lease(fd, lease_type) < 0) {
> +		pr_perror("Can't set lease (fd %i)\n", fd);
> +		return -1;
> +	}
> +	return 0;
> +}
> +
> +static struct fdinfo_list_entry *find_fd_unordered(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_broken_file_lease(FileLockEntry *fle)
> +{
> +	struct fdinfo_list_entry *fdle;
> +
> +	fdle = find_fd_unordered(current, fle->fd);
> +	if (fdle == NULL) {
> +		pr_err("Can't get file description\n");
> +		return -1;
> +	}
> +
> +	if (restore_lease_prebroken_state(fle->fd, fdle->desc->ops->type))
> +		return -1;
> +
> +	/*
> +	 * It could be broken by 2 types of open call:
> +	 * 1. non-blocking: It failed because of the lease.
> +	 * 2. blocking: It had been blocked at the moment
> +	 * of dumping, otherwise lease wouldn't be broken.
> +	 * Thus, it was canceled by CRIU.
> +	 *
> +	 * There are no files or leases in image, which will
> +	 * conflict with each other. Therefore we should explicitly
> +	 * break leases. Restoring can be done in any order.
> +	 */
> +	return break_lease(fle->type, fdle->desc);
> +}
> +
> +static int restore_file_lease(FileLockEntry *fle)
> +{
> +	int ret;
> +
> +	if (fle->type & LEASE_BREAKING) {
> +		return restore_broken_file_lease(fle);
> +	} else {
> +		ret = set_file_lease(fle->fd, fle->type);
> +		if (ret < 0)
> +			pr_perror("Can't restore non broken lease");
> +		return ret;
> +	}
> +}
> +
>  static int restore_file_lock(FileLockEntry *fle)
>  {
>  	int ret = -1;
> @@ -399,11 +560,9 @@ static int restore_file_lock(FileLockEntry *fle)
>  			goto err;
>  		}
>  	} else if (fle->flag & FL_LEASE) {
> -		ret = fcntl(fle->fd, F_SETLEASE, fle->type);
> -		if (ret < 0) {
> -			pr_perror("Can't set lease!\n");
> +		ret = restore_file_lease(fle);
> +		if (ret < 0)
>  			goto err;
> -		}
>  	} else {
>  		pr_err("Unknown file lock style!\n");
>  		goto err;
> diff --git a/criu/files.c b/criu/files.c
> index 01cd4c0e9..c103cbc1d 100644
> --- a/criu/files.c
> +++ b/criu/files.c
> @@ -458,6 +458,9 @@ static int dump_one_file(struct pid *pid, int fd, int lfd, struct fd_opts *opts,
>  	if (note_file_lock(pid, fd, lfd, &p))
>  		return -1;
>  
> +	if (correct_file_leases_type(pid, fd, lfd))
> +		return -1;
> +
>  	p.fd_ctl = ctl; /* Some dump_opts require this to talk to parasite */
>  
>  	if (S_ISSOCK(p.stat.st_mode))
> diff --git a/criu/include/file-lock.h b/criu/include/file-lock.h
> index f70739adb..7b19524d7 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;
> @@ -65,6 +68,7 @@ extern struct collect_image_info file_locks_cinfo;
>  
>  struct pid;
>  struct fd_parms;
> +extern int correct_file_leases_type(struct pid *, int fd, int lfd);
>  extern int note_file_lock(struct pid *, int fd, int lfd, struct fd_parms *);
>  extern int dump_file_locks(void);
>  
> 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;
> -- 
> 2.11.1
> 
> _______________________________________________
> CRIU mailing list
> CRIU@openvz.org
> https://lists.openvz.org/mailman/listinfo/criu
Andrei Vagin Sept. 7, 2017, 5:37 p.m.
On Thu, Sep 07, 2017 at 11:05:27AM +0300, Pavel Begunkov wrote:
> Leases can be taken out only on regular files. In order to restore
> breaking lease it break restored lease by opening a file with which
> lease is associated.
> 
> Also, the patch fix type of broken leases to 'target lease type',
> because procfs always returns 'READ' in this case.
> 
> #39
> Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
> ---
>  criu/file-lock.c         | 167 +++++++++++++++++++++++++++++++++++++++++++++--
>  criu/files.c             |   3 +
>  criu/include/file-lock.h |   4 ++
>  criu/proc_parse.c        |   7 +-
>  4 files changed, 174 insertions(+), 7 deletions(-)
> 
> diff --git a/criu/file-lock.c b/criu/file-lock.c
> index 92d8bd394..3f189a113 100644
> --- a/criu/file-lock.c
> +++ b/criu/file-lock.c
> @@ -1,6 +1,7 @@
>  #include <stdlib.h>
>  #include <unistd.h>
>  #include <sys/file.h>
> +#include <sys/fsuid.h>
>  #include <fcntl.h>
>  #include <string.h>
>  #include <sys/types.h>
> @@ -18,6 +19,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 +335,164 @@ int note_file_lock(struct pid *pid, int fd, int lfd, struct fd_parms *p)
>  	return 0;
>  }
>  
> +int correct_file_leases_type(struct pid *pid, int fd, int lfd)
> +{
> +	struct file_lock *fl;
> +	int target_type;
> +
> +	list_for_each_entry(fl, &file_lock_list, list) {
> +		/* owners_fd should be set before usage */
> +		if (fl->fl_owner != pid->real || fl->owners_fd != fd)
> +			continue;
> +
> +		if (fl->fl_kind == FL_LEASE &&
> +			(fl->fl_ltype & LEASE_BREAKING)) {
> +			/*
> +			 * Set lease type to actual 'target lease type'
> +			 * instead of 'READ' returned by procfs.
> +			 */
> +			target_type = fcntl(lfd, F_GETLEASE);
> +			if (target_type < 0) {
> +				perror("Can't get lease type\n");
> +				return -1;
> +			}
> +
> +			fl->fl_ltype &= ~O_ACCMODE;
> +			fl->fl_ltype |= target_type;
> +			break;
> +		}
> +	}
> +	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);

I think this call will queue a signal (SIGIO) to the current process.
And we probably has to handle it, because criu restores queued signals.

> +	if (fd >= 0) {
> +		pr_err("Conflicting lease wasn't found\n");
> +		close(fd);
> +		return -1;
> +	}
> +	if (errno != EWOULDBLOCK) {
> +		pr_perror("Can't break lease\n");
> +		return -1;
> +	}
> +	return 0;
> +}
> +
> +static int break_lease(int lease_type, struct file_desc *desc)
> +{
> +	int target_type = lease_type & (~LEASE_BREAKING);
> +	int break_flags;
> +
> +	/*
> +	 * Flags for open call chosen in a way to even
> +	 * 'target lease type' returned by fcntl(F_GETLEASE)
> +	 * and lease type from the image.
> +	 */
> +	if (target_type == F_UNLCK) {
> +		break_flags = O_WRONLY;
> +	} else if (target_type == F_RDLCK) {
> +		break_flags = O_RDONLY;
> +	} else {
> +		pr_err("Incorrect target lease type\n");
> +		return -1;
> +	}
> +	return open_path(desc, open_break_cb, (void *)&break_flags);
> +}
> +
> +static int set_file_lease(int fd, int type)
> +{
> +	int old_fsuid, ret;
> +	struct stat st;
> +
> +	if (fstat(fd, &st)) {
> +		pr_perror("Can't get file stat (%i)\n", fd);
> +		return -1;
> +	}
> +
> +	/*
> +	 * An unprivileged process may take out a lease only if
> +	 * uid of the file matches the fsuid of the process.
> +	 */
> +	old_fsuid = setfsuid(st.st_uid);
> +
> +	ret = fcntl(fd, F_SETLEASE, type);
> +	if (ret < 0)
> +		pr_perror("Can't set lease\n");
> +
> +	setfsuid(old_fsuid);
> +	return ret;
> +}
> +
> +static int restore_lease_prebroken_state(int fd, int fd_type)
> +{
> +	int access_flags = fd_type & O_ACCMODE;
> +	int lease_type = (access_flags == O_RDONLY) ? F_RDLCK : F_WRLCK;
> +
> +	if (set_file_lease(fd, lease_type) < 0) {
> +		pr_perror("Can't set lease (fd %i)\n", fd);
> +		return -1;
> +	}
> +	return 0;
> +}
> +
> +static struct fdinfo_list_entry *find_fd_unordered(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_broken_file_lease(FileLockEntry *fle)
> +{
> +	struct fdinfo_list_entry *fdle;
> +
> +	fdle = find_fd_unordered(current, fle->fd);
> +	if (fdle == NULL) {
> +		pr_err("Can't get file description\n");
> +		return -1;
> +	}
> +
> +	if (restore_lease_prebroken_state(fle->fd, fdle->desc->ops->type))
> +		return -1;
> +
> +	/*
> +	 * It could be broken by 2 types of open call:
> +	 * 1. non-blocking: It failed because of the lease.
> +	 * 2. blocking: It had been blocked at the moment
> +	 * of dumping, otherwise lease wouldn't be broken.
> +	 * Thus, it was canceled by CRIU.
> +	 *
> +	 * There are no files or leases in image, which will
> +	 * conflict with each other. Therefore we should explicitly
> +	 * break leases. Restoring can be done in any order.
> +	 */
> +	return break_lease(fle->type, fdle->desc);
> +}
> +
> +static int restore_file_lease(FileLockEntry *fle)
> +{
> +	int ret;
> +
> +	if (fle->type & LEASE_BREAKING) {
> +		return restore_broken_file_lease(fle);
> +	} else {
> +		ret = set_file_lease(fle->fd, fle->type);
> +		if (ret < 0)
> +			pr_perror("Can't restore non broken lease");
> +		return ret;
> +	}
> +}
> +
>  static int restore_file_lock(FileLockEntry *fle)
>  {
>  	int ret = -1;
> @@ -399,11 +560,9 @@ static int restore_file_lock(FileLockEntry *fle)
>  			goto err;
>  		}
>  	} else if (fle->flag & FL_LEASE) {
> -		ret = fcntl(fle->fd, F_SETLEASE, fle->type);
> -		if (ret < 0) {
> -			pr_perror("Can't set lease!\n");
> +		ret = restore_file_lease(fle);
> +		if (ret < 0)
>  			goto err;
> -		}
>  	} else {
>  		pr_err("Unknown file lock style!\n");
>  		goto err;
> diff --git a/criu/files.c b/criu/files.c
> index 01cd4c0e9..c103cbc1d 100644
> --- a/criu/files.c
> +++ b/criu/files.c
> @@ -458,6 +458,9 @@ static int dump_one_file(struct pid *pid, int fd, int lfd, struct fd_opts *opts,
>  	if (note_file_lock(pid, fd, lfd, &p))
>  		return -1;
>  
> +	if (correct_file_leases_type(pid, fd, lfd))
> +		return -1;
> +
>  	p.fd_ctl = ctl; /* Some dump_opts require this to talk to parasite */
>  
>  	if (S_ISSOCK(p.stat.st_mode))
> diff --git a/criu/include/file-lock.h b/criu/include/file-lock.h
> index f70739adb..7b19524d7 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;
> @@ -65,6 +68,7 @@ extern struct collect_image_info file_locks_cinfo;
>  
>  struct pid;
>  struct fd_parms;
> +extern int correct_file_leases_type(struct pid *, int fd, int lfd);
>  extern int note_file_lock(struct pid *, int fd, int lfd, struct fd_parms *);
>  extern int dump_file_locks(void);
>  
> 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;
> -- 
> 2.11.1
> 
> _______________________________________________
> CRIU mailing list
> CRIU@openvz.org
> https://lists.openvz.org/mailman/listinfo/criu