file_write: Fix NULL pointer dereference.
authorAndre Noll <maan@systemlinux.org>
Thu, 4 Nov 2010 22:34:33 +0000 (23:34 +0100)
committerAndre Noll <maan@systemlinux.org>
Tue, 23 Nov 2010 16:39:11 +0000 (17:39 +0100)
register_writer_node() is the only caller of the ->open method for paraslash
writers. It does not check its return value, which is OK for alsa, oss and osx
as these writer's ->open method always succeeds. However, the ->open() method
of the file writer may fail, for example because the output file could not be
opened. This error will be ignored and the writer node task is registered as
usual with ->fd being initialized to zero.

Fix this bug by splitting the ->open method of the file writer into the part which
merely allocates the private_file_write_data structure, hence always succeeds,
and the part which actually opens the output file. This second part is called later
from ->post_select as soon as there is data available to be written.

After this patch the ->open methods of all writers always succeed and we
may change its return value to void which is done in the next patch.

file_write.c

index 4a4dc66..7497dfa 100644 (file)
@@ -52,9 +52,17 @@ __must_check __malloc static char *random_filename(void)
 
 static int file_write_open(struct writer_node *wn)
 {
-       struct private_file_write_data *pfwd = para_calloc(
-               sizeof(struct private_file_write_data));
+       struct private_file_write_data *pfwd = para_calloc(sizeof(*pfwd));
+
+       wn->private_data = pfwd;
+       pfwd->fd = -1;
+       return 0;
+}
+
+static int prepare_output_file(struct writer_node *wn)
+{
        struct file_write_args_info *conf = wn->conf;
+       struct private_file_write_data *pfwd = wn->private_data;
        char *filename;
        int ret;
 
@@ -62,7 +70,6 @@ static int file_write_open(struct writer_node *wn)
                filename = conf->filename_arg;
        else
                filename = random_filename();
-       wn->private_data = pfwd;
        ret = para_open(filename, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
        if (!conf->filename_given)
                free(filename);
@@ -86,9 +93,9 @@ static void file_write_pre_select(struct sched *s, struct task *t)
 
        t->error = 0;
        ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF);
-       if (ret > 0)
+       if (ret > 0 && pfwd->fd >= 0)
                para_fd_set(pfwd->fd, &s->wfds, &s->max_fileno);
-       else if (ret < 0)
+       else if (ret != 0) /* error or bos and fd not yet open */
                sched_min_delay(s);
 }
 
@@ -96,7 +103,8 @@ static void file_write_close(struct writer_node *wn)
 {
        struct private_file_write_data *pfwd = wn->private_data;
 
-       close(pfwd->fd);
+       if (pfwd->fd >= 0)
+               close(pfwd->fd);
        free(pfwd);
 }
 
@@ -114,6 +122,11 @@ static void file_write_post_select(__a_unused struct sched *s,
        ret = btr_node_status(btrn, wn->min_iqs, BTR_NT_LEAF);
        if (ret <= 0)
                goto out;
+       if (pfwd->fd < 0) {
+               ret = prepare_output_file(wn);
+               if (ret < 0)
+                       goto out;
+       }
        if (!FD_ISSET(pfwd->fd, &s->wfds))
                return;
        bytes = btr_next_buffer(btrn, &buf);