mirror of
https://github.com/checkpoint-restore/criu
synced 2025-09-03 15:55:53 +00:00
crtools: collect and check file locks
We collect all file locks to a golbal list, so we can use them easily in dump_one_task. For optimizaton, we only collect file locks hold by tasks in the pstree. Thanks to the ptrace-seize machanism, we can aviod the blocked file lock issue, makes the work simpler. Right now, the check handles only one situation: -- Dumping tasks with file locks hold without the -l option. This covers for the most part. But we still need some more work to make it perfect robust in the future. Signed-off-by: Qiang Huang <h.huangqiang@huawei.com> Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
This commit is contained in:
committed by
Pavel Emelyanov
parent
b08836cc51
commit
286801d4c1
1
Makefile
1
Makefile
@@ -123,6 +123,7 @@ OBJS += protobuf.o
|
||||
OBJS += tty.o
|
||||
OBJS += cr-exec.o
|
||||
OBJS += cpu.o
|
||||
OBJS += file-lock.o
|
||||
OBJS += $(ARCH_DIR)/crtools.o
|
||||
|
||||
DEPS := $(patsubst %.o,%.d,$(OBJS))
|
||||
|
40
cr-dump.c
40
cr-dump.c
@@ -29,6 +29,7 @@
|
||||
#include "protobuf/mm.pb-c.h"
|
||||
#include "protobuf/creds.pb-c.h"
|
||||
#include "protobuf/core.pb-c.h"
|
||||
#include "protobuf/file-lock.pb-c.h"
|
||||
#include "protobuf/rlimit.pb-c.h"
|
||||
|
||||
#include "asm/types.h"
|
||||
@@ -64,6 +65,7 @@
|
||||
#include "cpu.h"
|
||||
#include "fpu.h"
|
||||
#include "elf.h"
|
||||
#include "file-lock.h"
|
||||
|
||||
#include "asm/dump.h"
|
||||
|
||||
@@ -1204,6 +1206,40 @@ try_again:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int collect_file_locks(const struct cr_options *opts)
|
||||
{
|
||||
if (parse_file_locks())
|
||||
return -1;
|
||||
|
||||
if (opts->handle_file_locks)
|
||||
/*
|
||||
* If the handle file locks option is set,
|
||||
* collect work is over.
|
||||
*/
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* If the handle file locks option is not set, we need to do
|
||||
* the check, any file locks hold by tasks in our pstree is
|
||||
* not allowed.
|
||||
*
|
||||
* It's hard to do it carefully, there might be some other
|
||||
* issues like tasks beyond pstree would use flocks hold by
|
||||
* dumping tasks, but we can't know it in dumping time.
|
||||
* We need to make sure these flocks only used by dumping tasks.
|
||||
* We might have to do the check that this option would only
|
||||
* be used by container dumping.
|
||||
*/
|
||||
if (!list_empty(&file_lock_list)) {
|
||||
pr_perror("Some file locks are hold by dumping tasks!"
|
||||
"You can try -l to dump them.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static int dump_task_thread(struct parasite_ctl *parasite_ctl, struct pid *tid)
|
||||
{
|
||||
CoreEntry *core;
|
||||
@@ -1568,6 +1604,9 @@ int cr_dump_tasks(pid_t pid, const struct cr_options *opts)
|
||||
if (collect_pstree(pid, opts))
|
||||
goto err;
|
||||
|
||||
if (collect_file_locks(opts))
|
||||
goto err;
|
||||
|
||||
if (collect_mount_info())
|
||||
goto err;
|
||||
|
||||
@@ -1633,6 +1672,7 @@ err:
|
||||
pstree_switch_state(root_item,
|
||||
ret ? TASK_ALIVE : opts->final_state);
|
||||
free_pstree(root_item);
|
||||
free_file_locks();
|
||||
|
||||
close_safe(&pidns_proc);
|
||||
|
||||
|
30
file-lock.c
Normal file
30
file-lock.c
Normal file
@@ -0,0 +1,30 @@
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "file-lock.h"
|
||||
|
||||
struct list_head file_lock_list = LIST_HEAD_INIT(file_lock_list);
|
||||
|
||||
struct file_lock *alloc_file_lock(void)
|
||||
{
|
||||
struct file_lock *flock;
|
||||
|
||||
flock = xzalloc(sizeof(*flock));
|
||||
if (!flock)
|
||||
return NULL;
|
||||
|
||||
INIT_LIST_HEAD(&flock->list);
|
||||
|
||||
return flock;
|
||||
}
|
||||
|
||||
void free_file_locks(void)
|
||||
{
|
||||
struct file_lock *flock, *tmp;
|
||||
|
||||
list_for_each_entry_safe(flock, tmp, &file_lock_list, list) {
|
||||
xfree(flock);
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&file_lock_list);
|
||||
}
|
28
include/file-lock.h
Normal file
28
include/file-lock.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef __FILE_LOCK_H__
|
||||
#define __FILE_LOCK_H__
|
||||
|
||||
#include "crtools.h"
|
||||
#include "protobuf.h"
|
||||
#include "../protobuf/file-lock.pb-c.h"
|
||||
|
||||
struct file_lock {
|
||||
long long fl_id;
|
||||
char fl_flag[10];
|
||||
char fl_type[15];
|
||||
char fl_option[10];
|
||||
|
||||
pid_t fl_owner;
|
||||
int maj, min;
|
||||
unsigned long i_no;
|
||||
long long start;
|
||||
char end[32];
|
||||
|
||||
struct list_head list; /* list of all file locks */
|
||||
};
|
||||
|
||||
extern struct list_head file_lock_list;
|
||||
|
||||
extern struct file_lock *alloc_file_lock(void);
|
||||
extern void free_file_locks(void);
|
||||
|
||||
#endif /* __FILE_LOCK_H__ */
|
@@ -140,5 +140,6 @@ union fdinfo_entries {
|
||||
extern int parse_fdinfo(int fd, int type,
|
||||
int (*cb)(union fdinfo_entries *e, void *arg), void *arg);
|
||||
extern int parse_cpuinfo_features(void);
|
||||
extern int parse_file_locks(void);
|
||||
|
||||
#endif /* __CR_PROC_PARSE_H__ */
|
||||
|
95
proc_parse.c
95
proc_parse.c
@@ -15,6 +15,8 @@
|
||||
#include "crtools.h"
|
||||
#include "mount.h"
|
||||
#include "cpu.h"
|
||||
#include "file-lock.h"
|
||||
#include "pstree.h"
|
||||
#include "fsnotify.h"
|
||||
|
||||
#include "proc_parse.h"
|
||||
@@ -1063,3 +1065,96 @@ parse_err:
|
||||
pr_perror("%s: error parsing [%s] for %d\n", __func__, str, type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int parse_file_lock_buf(char *buf, struct file_lock *fl,
|
||||
bool is_blocked)
|
||||
{
|
||||
int num;
|
||||
|
||||
if (is_blocked) {
|
||||
num = sscanf(buf, "%lld: -> %s %s %s %d %02x:%02x:%ld %lld %s",
|
||||
&fl->fl_id, fl->fl_flag, fl->fl_type, fl->fl_option,
|
||||
&fl->fl_owner, &fl->maj, &fl->min, &fl->i_no,
|
||||
&fl->start, fl->end);
|
||||
} else {
|
||||
num = sscanf(buf, "%lld:%s %s %s %d %02x:%02x:%ld %lld %s",
|
||||
&fl->fl_id, fl->fl_flag, fl->fl_type, fl->fl_option,
|
||||
&fl->fl_owner, &fl->maj, &fl->min, &fl->i_no,
|
||||
&fl->start, fl->end);
|
||||
}
|
||||
|
||||
if (num < 10) {
|
||||
pr_perror("Invalid file lock info!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int parse_file_locks(void)
|
||||
{
|
||||
struct file_lock *fl;
|
||||
|
||||
FILE *fl_locks;
|
||||
int ret = 0;
|
||||
bool is_blocked = false;
|
||||
|
||||
fl_locks = fopen("/proc/locks", "r");
|
||||
if (!fl_locks) {
|
||||
pr_perror("Can't open file locks file!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (fgets(buf, BUF_SIZE, fl_locks)) {
|
||||
if (strstr(buf, "->"))
|
||||
is_blocked = true;
|
||||
|
||||
fl = alloc_file_lock();
|
||||
if (!fl) {
|
||||
pr_perror("Alloc file lock failed!");
|
||||
ret = -1;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (parse_file_lock_buf(buf, fl, is_blocked)) {
|
||||
xfree(fl);
|
||||
ret = -1;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!pid_in_pstree(fl->fl_owner)) {
|
||||
/*
|
||||
* We only care about tasks which are taken
|
||||
* into dump, so we only collect file locks
|
||||
* belong to these tasks.
|
||||
*/
|
||||
xfree(fl);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_blocked) {
|
||||
/*
|
||||
* Here the task is in the pstree.
|
||||
* If it is blocked on a flock, when we try to
|
||||
* ptrace-seize it, the kernel will unblock task
|
||||
* from flock and will stop it in another place.
|
||||
* So in dumping, a blocked file lock should never
|
||||
* be here.
|
||||
*/
|
||||
pr_perror("We have a blocked file lock!");
|
||||
ret = -1;
|
||||
goto err;
|
||||
}
|
||||
|
||||
pr_info("lockinfo: %lld:%s %s %s %d %02x:%02x:%ld %lld %s\n",
|
||||
fl->fl_id, fl->fl_flag, fl->fl_type, fl->fl_option,
|
||||
fl->fl_owner, fl->maj, fl->min, fl->i_no,
|
||||
fl->start, fl->end);
|
||||
|
||||
list_add_tail(&fl->list, &file_lock_list);
|
||||
}
|
||||
|
||||
err:
|
||||
fclose(fl_locks);
|
||||
return ret;
|
||||
}
|
||||
|
Reference in New Issue
Block a user