diff --git a/Makefile b/Makefile index 04ac3c1a1..d20384501 100644 --- a/Makefile +++ b/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)) diff --git a/cr-dump.c b/cr-dump.c index 4a1e9e9a1..c4bbcdf4d 100644 --- a/cr-dump.c +++ b/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); diff --git a/file-lock.c b/file-lock.c new file mode 100644 index 000000000..276007904 --- /dev/null +++ b/file-lock.c @@ -0,0 +1,30 @@ +#include +#include + +#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); +} diff --git a/include/file-lock.h b/include/file-lock.h new file mode 100644 index 000000000..9fdaa3973 --- /dev/null +++ b/include/file-lock.h @@ -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__ */ diff --git a/include/proc_parse.h b/include/proc_parse.h index 73878d369..ae8a71c63 100644 --- a/include/proc_parse.h +++ b/include/proc_parse.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__ */ diff --git a/proc_parse.c b/proc_parse.c index 83c4ef96e..68b7523ff 100644 --- a/proc_parse.c +++ b/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; +}