git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH] gc: use temporary file for editing crontab

From
Johannes Schindelin <johannes.schindelin@gmx.de>
Date
Aug 23, 2022, 09:12 UTC
Message-ID
<6428252p-ssrn-7qs7-9p26-5so10r96s3os@tzk.qr>
In-Reply-To
<20220823010120.25388-1-sandals@crustytoothpaste.net>
Hi brian,
On Tue, 23 Aug 2022, brian m. carlson wrote:
Show 13 quoted lines
> While cron is specified by POSIX, there are a wide variety of
> implementations in use.  On FreeBSD, the cron implementation requires a
> file name argument: if the user wants to edit standard input, they must
> specify "-".  However, this notation is not specified by POSIX, allowing
> the possibility that making such a change may break other, less common
> implementations.
>
> Since POSIX tells us that cron must accept a file name argument, let's
> solve this problem by specifying a temporary file instead.  This will
> ensure that we work with the vast majority of implementations.
>
> Reported-by: Renato Botelho <garga@FreeBSD.org>
> Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Beautiful commit message. Thank you!
Show 19 quoted lines
> diff --git a/builtin/gc.c b/builtin/gc.c
> index eeff2b760e..168dbdb5d9 100644
> --- a/builtin/gc.c
> +++ b/builtin/gc.c
> @@ -2065,6 +2065,7 @@ static int crontab_update_schedule(int run_maintenance, int fd)
>  	struct child_process crontab_edit = CHILD_PROCESS_INIT;
>  	FILE *cron_list, *cron_in;
>  	struct strbuf line = STRBUF_INIT;
> +	struct tempfile *tmpedit;
>
>  	get_schedule_cmd(&cmd, NULL);
>  	strvec_split(&crontab_list.args, cmd);
> @@ -2079,6 +2080,15 @@ static int crontab_update_schedule(int run_maintenance, int fd)
>  	/* Ignore exit code, as an empty crontab will return error. */
>  	finish_command(&crontab_list);
>
> +	tmpedit = mks_tempfile_t(".git_cron_edit_tmpXXXXXX");
> +	if (!tmpedit)
> +		return error(_("failed to create crontab temporary file"));

It might make sense to use the same `goto out;` pattern here, to make it easier to reason about the early exit even six years from now.

We do not even have to guard the `close_tempfile_gently()` behind an `if (tempfile)` conditional because that function handles `NULL` parameters gently.

Show 35 quoted lines
> +	cron_in = fdopen_tempfile(tmpedit, "w");
> +	if (!cron_in) {
> +		result = error(_("failed to open temporary file"));
> +		goto out;
> +	}
> +
>  	/*
>  	 * Read from the .lock file, filtering out the old
>  	 * schedule while appending the new schedule.
> @@ -2086,19 +2096,6 @@ static int crontab_update_schedule(int run_maintenance, int fd)
>  	cron_list = fdopen(fd, "r");
>  	rewind(cron_list);
>
> -	strvec_split(&crontab_edit.args, cmd);
> -	crontab_edit.in = -1;
> -	crontab_edit.git_cmd = 0;
> -
> -	if (start_command(&crontab_edit))
> -		return error(_("failed to run 'crontab'; your system might not support 'cron'"));
> -
> -	cron_in = fdopen(crontab_edit.in, "w");
> -	if (!cron_in) {
> -		result = error(_("failed to open stdin of 'crontab'"));
> -		goto done_editing;
> -	}
> -
>  	while (!strbuf_getline_lf(&line, cron_list)) {
>  		if (!in_old_region && !strcmp(line.buf, BEGIN_LINE))
>  			in_old_region = 1;
> @@ -2132,14 +2129,22 @@ static int crontab_update_schedule(int run_maintenance, int fd)
>  	}
>
>  	fflush(cron_in);
> -	fclose(cron_in);
> -	close(crontab_edit.in);

This worries me a bit. I could imagine that keeping the file open and then expecting a spawned process to read its stdin from that file won't work on Windows.

In any case, I would consider it the correct thing to do to close the temp file here. In other words, I would like to move the `close_tempfile_gently()` call to this location.

Show 17 quoted lines
>
> -done_editing:
> +	strvec_split(&crontab_edit.args, cmd);
> +	strvec_push(&crontab_edit.args, get_tempfile_path(tmpedit));
> +	crontab_edit.git_cmd = 0;
> +
> +	if (start_command(&crontab_edit)) {
> +		result = error(_("failed to run 'crontab'; your system might not support 'cron'"));
> +		goto out;
> +	}
> +
>  	if (finish_command(&crontab_edit))
>  		result = error(_("'crontab' died"));
>  	else
>  		fclose(cron_list);
> +out:
> +	close_tempfile_gently(tmpedit);

Here, I would like to call `delete_tempfile(&tmpedit);` instead. That way, the memory is released correctly, the temporary file is deleted, and everything is neatly cleaned up.

The way I read the code, `delete_tempfile(&tmpedit)` would return early if `tmpedit == NULL`, and otherwise clean everything up and release the memory, so there is no need to guard this call behind an `if (tmpedit)` conditional.

Side note: I do notice that `delete_tempfile(&tmpedit)` seems to _not_ release memory when `tmpedit` is non-NULL when `tmpedit->active == 0`. I consider this a bug in the `delete_tempfile()` code (in its `if (!is_tempfile_active(tempfile))` clause, it should call `deactivate_tempfile()` for non-NULL `tempfile`s and set `*tempfile_p = NULL;`), but it is outside the scope of your patch to address that.

What do you think about my suggestions?

Thanks, Dscho

>  	return result;
>  }
>
>
Previous: brian m. carlsonNext: Derrick Stolee
Message 11 of 19 in “git maintenance broken on FreeBSD”
  1. Renato BotelhoAug 12, 2022
  2. Đoàn Trần Công DanhAug 12, 2022
  3. Todd ZullingerAug 13, 2022
  4. Junio C HamanoAug 13, 2022
  5. Đoàn Trần Công DanhAug 13, 2022
  6. Junio C HamanoAug 13, 2022
  7. brian m. carlsonAug 13, 2022
  8. Derrick StoleeAug 15, 2022
  9. Junio C HamanoAug 15, 2022
  10. gc: use temporary file for editing crontabbrian m. carlson, Aug 23, 2022
  11. Johannes SchindelinAug 23, 2022
  12. Derrick StoleeAug 23, 2022
  13. brian m. carlsonAug 23, 2022
  14. Junio C HamanoAug 24, 2022
  15. gc: use temporary file for editing crontabbrian m. carlson, Aug 28, 2022
  16. Junio C HamanoAug 29, 2022
  17. Renato BotelhoAug 29, 2022
  18. Derrick StoleeAug 30, 2022
  19. test-crontab: minor memory and error handling fixesJeff King, Aug 30, 2022

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.