OSDN Git Service

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[uclinux-h8/linux.git] / fs / coredump.c
index a987f3d..a9abe31 100644 (file)
@@ -263,7 +263,6 @@ static int zap_process(struct task_struct *start, int exit_code)
        struct task_struct *t;
        int nr = 0;
 
-       start->signal->flags = SIGNAL_GROUP_EXIT;
        start->signal->group_exit_code = exit_code;
        start->signal->group_stop_count = 0;
 
@@ -280,8 +279,8 @@ static int zap_process(struct task_struct *start, int exit_code)
        return nr;
 }
 
-static inline int zap_threads(struct task_struct *tsk, struct mm_struct *mm,
-                               struct core_state *core_state, int exit_code)
+static int zap_threads(struct task_struct *tsk, struct mm_struct *mm,
+                       struct core_state *core_state, int exit_code)
 {
        struct task_struct *g, *p;
        unsigned long flags;
@@ -291,11 +290,16 @@ static inline int zap_threads(struct task_struct *tsk, struct mm_struct *mm,
        if (!signal_group_exit(tsk->signal)) {
                mm->core_state = core_state;
                nr = zap_process(tsk, exit_code);
+               tsk->signal->group_exit_task = tsk;
+               /* ignore all signals except SIGKILL, see prepare_signal() */
+               tsk->signal->flags = SIGNAL_GROUP_COREDUMP;
+               clear_tsk_thread_flag(tsk, TIF_SIGPENDING);
        }
        spin_unlock_irq(&tsk->sighand->siglock);
        if (unlikely(nr < 0))
                return nr;
 
+       tsk->flags = PF_DUMPCORE;
        if (atomic_read(&mm->mm_users) == nr + 1)
                goto done;
        /*
@@ -340,6 +344,7 @@ static inline int zap_threads(struct task_struct *tsk, struct mm_struct *mm,
                                if (unlikely(p->mm == mm)) {
                                        lock_task_sighand(p, &flags);
                                        nr += zap_process(p, exit_code);
+                                       p->signal->flags = SIGNAL_GROUP_EXIT;
                                        unlock_task_sighand(p, &flags);
                                }
                                break;
@@ -386,11 +391,18 @@ static int coredump_wait(int exit_code, struct core_state *core_state)
        return core_waiters;
 }
 
-static void coredump_finish(struct mm_struct *mm)
+static void coredump_finish(struct mm_struct *mm, bool core_dumped)
 {
        struct core_thread *curr, *next;
        struct task_struct *task;
 
+       spin_lock_irq(&current->sighand->siglock);
+       if (core_dumped && !__fatal_signal_pending(current))
+               current->signal->group_exit_code |= 0x80;
+       current->signal->group_exit_task = NULL;
+       current->signal->flags = SIGNAL_GROUP_EXIT;
+       spin_unlock_irq(&current->sighand->siglock);
+
        next = mm->core_state->dumper.next;
        while ((curr = next) != NULL) {
                next = curr->next;
@@ -407,6 +419,17 @@ static void coredump_finish(struct mm_struct *mm)
        mm->core_state = NULL;
 }
 
+static bool dump_interrupted(void)
+{
+       /*
+        * SIGKILL or freezing() interrupt the coredumping. Perhaps we
+        * can do try_to_freeze() and check __fatal_signal_pending(),
+        * but then we need to teach dump_write() to restart and clear
+        * TIF_SIGPENDING.
+        */
+       return signal_pending(current);
+}
+
 static void wait_for_dump_helpers(struct file *file)
 {
        struct pipe_inode_info *pipe = file->private_data;
@@ -414,17 +437,20 @@ static void wait_for_dump_helpers(struct file *file)
        pipe_lock(pipe);
        pipe->readers++;
        pipe->writers--;
+       wake_up_interruptible_sync(&pipe->wait);
+       kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN);
+       pipe_unlock(pipe);
 
-       while ((pipe->readers > 1) && (!signal_pending(current))) {
-               wake_up_interruptible_sync(&pipe->wait);
-               kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN);
-               pipe_wait(pipe);
-       }
+       /*
+        * We actually want wait_event_freezable() but then we need
+        * to clear TIF_SIGPENDING and improve dump_interrupted().
+        */
+       wait_event_interruptible(pipe->wait, pipe->readers == 1);
 
+       pipe_lock(pipe);
        pipe->readers--;
        pipe->writers++;
        pipe_unlock(pipe);
-
 }
 
 /*
@@ -469,6 +495,7 @@ void do_coredump(siginfo_t *siginfo)
        int ispipe;
        struct files_struct *displaced;
        bool need_nonrelative = false;
+       bool core_dumped = false;
        static atomic_t core_dump_count = ATOMIC_INIT(0);
        struct coredump_params cprm = {
                .siginfo = siginfo,
@@ -512,17 +539,12 @@ void do_coredump(siginfo_t *siginfo)
 
        old_cred = override_creds(cred);
 
-       /*
-        * Clear any false indication of pending signals that might
-        * be seen by the filesystem code called to write the core file.
-        */
-       clear_thread_flag(TIF_SIGPENDING);
-
        ispipe = format_corename(&cn, &cprm);
 
-       if (ispipe) {
+       if (ispipe) {
                int dump_count;
                char **helper_argv;
+               struct subprocess_info *sub_info;
 
                if (ispipe < 0) {
                        printk(KERN_WARNING "format_corename failed\n");
@@ -569,15 +591,20 @@ void do_coredump(siginfo_t *siginfo)
                        goto fail_dropcount;
                }
 
-               retval = call_usermodehelper_fns(helper_argv[0], helper_argv,
-                                       NULL, UMH_WAIT_EXEC, umh_pipe_setup,
-                                       NULL, &cprm);
+               retval = -ENOMEM;
+               sub_info = call_usermodehelper_setup(helper_argv[0],
+                                               helper_argv, NULL, GFP_KERNEL,
+                                               umh_pipe_setup, NULL, &cprm);
+               if (sub_info)
+                       retval = call_usermodehelper_exec(sub_info,
+                                                         UMH_WAIT_EXEC);
+
                argv_free(helper_argv);
                if (retval) {
-                       printk(KERN_INFO "Core dump to %s pipe failed\n",
+                       printk(KERN_INFO "Core dump to %s pipe failed\n",
                               cn.corename);
                        goto close_fail;
-               }
+               }
        } else {
                struct inode *inode;
 
@@ -628,9 +655,7 @@ void do_coredump(siginfo_t *siginfo)
        if (displaced)
                put_files_struct(displaced);
        file_start_write(cprm.file);
-       retval = binfmt->core_dump(&cprm);
-       if (retval)
-               current->signal->group_exit_code |= 0x80;
+       core_dumped = !dump_interrupted() && binfmt->core_dump(&cprm);
        file_end_write(cprm.file);
 
        if (ispipe && core_pipe_limit)
@@ -644,7 +669,7 @@ fail_dropcount:
 fail_unlock:
        kfree(cn.corename);
 fail_corename:
-       coredump_finish(mm);
+       coredump_finish(mm, core_dumped);
        revert_creds(old_cred);
 fail_creds:
        put_cred(cred);
@@ -659,7 +684,9 @@ fail:
  */
 int dump_write(struct file *file, const void *addr, int nr)
 {
-       return access_ok(VERIFY_READ, addr, nr) && file->f_op->write(file, addr, nr, &file->f_pos) == nr;
+       return !dump_interrupted() &&
+               access_ok(VERIFY_READ, addr, nr) &&
+               file->f_op->write(file, addr, nr, &file->f_pos) == nr;
 }
 EXPORT_SYMBOL(dump_write);
 
@@ -668,7 +695,8 @@ int dump_seek(struct file *file, loff_t off)
        int ret = 1;
 
        if (file->f_op->llseek && file->f_op->llseek != no_llseek) {
-               if (file->f_op->llseek(file, off, SEEK_CUR) < 0)
+               if (dump_interrupted() ||
+                   file->f_op->llseek(file, off, SEEK_CUR) < 0)
                        return 0;
        } else {
                char *buf = (char *)get_zeroed_page(GFP_KERNEL);