"GOT", but the "O" is a cute, smiling pufferfish. Index | Thread | Search

From:
Tracey Emery <tracey@traceyemery.net>
Subject:
Re: move got_opentempfd() out of lib/diff.c again
To:
Stefan Sperling <stsp@stsp.name>
Cc:
gameoftrees@openbsd.org
Date:
Tue, 28 Jun 2022 14:31:49 -0600

Download raw body.

Thread
On Tue, Jun 28, 2022 at 10:08:10PM +0200, Stefan Sperling wrote:
> This moves all opentempfd() calls out of lib/diff.c.
> 
> There is a bug fix in here where we forgot to rewind a temporary
> file after truncating it. This results in ERR_PRIVSEP_LEN when
> we verify the size of an extracted blob (I saw this happen during
> 'got histedit').
> 
> ok?

ok! this was a majour improvement and a lot of work. thanks!

>  
> diff 7a13e6e501f6808283b956b84746cc9e8d2f1b25 c6ae84322fdabec2faa256572927c44d6d3c68ad
> commit - 7a13e6e501f6808283b956b84746cc9e8d2f1b25
> commit + c6ae84322fdabec2faa256572927c44d6d3c68ad
> blob - e4665492a3022c605cf1a1fa244f10c606b8b0b7
> blob + b07d18f8d9b4cc592948a9a6fdfe9d7504989ee3
> --- got/got.c
> +++ got/got.c
> @@ -3609,6 +3609,7 @@ diff_trees(struct got_object_id *tree_id1, struct got_
>  	struct got_tree_object *tree1 = NULL, *tree2 = NULL;
>  	struct got_diff_blob_output_unidiff_arg arg;
>  	FILE *f1 = NULL, *f2 = NULL;
> +	int fd1 = -1, fd2 = -1;
>  
>  	if (tree_id1) {
>  		err = got_object_open_as_tree(&tree1, repo, tree_id1);
> @@ -3619,6 +3620,12 @@ diff_trees(struct got_object_id *tree_id1, struct got_
>  			err = got_error_from_errno("got_opentemp");
>  			goto done;
>  		}
> +
> +		fd1 = got_opentempfd();
> +		if (fd1 == -1) {
> +			err = got_error_from_errno("got_opentempfd");
> +			goto done;
> +		}
>  	}
>  
>  	err = got_object_open_as_tree(&tree2, repo, tree_id2);
> @@ -3630,7 +3637,11 @@ diff_trees(struct got_object_id *tree_id1, struct got_
>  		err = got_error_from_errno("got_opentemp");
>  		goto done;
>  	}
> -
> +	fd2 = got_opentempfd();
> +	if (fd2 == -1) {
> +		err = got_error_from_errno("got_opentempfd");
> +		goto done;
> +	}
>  	arg.diff_context = diff_context;
>  	arg.ignore_whitespace = ignore_whitespace;
>  	arg.force_text_diff = force_text_diff;
> @@ -3639,7 +3650,7 @@ diff_trees(struct got_object_id *tree_id1, struct got_
>  	arg.nlines = 0;
>  	while (path[0] == '/')
>  		path++;
> -	err = got_diff_tree(tree1, tree2, f1, f2, path, path, repo,
> +	err = got_diff_tree(tree1, tree2, f1, f2, fd1, fd2, path, path, repo,
>  	    got_diff_blob_output_unidiff, &arg, 1);
>  done:
>  	if (tree1)
> @@ -3650,6 +3661,10 @@ done:
>  		err = got_error_from_errno("fclose");
>  	if (f2 && fclose(f2) == EOF && err == NULL)
>  		err = got_error_from_errno("fclose");
> +	if (fd1 != -1 && close(fd1) == -1 && err == NULL)
> +		err = got_error_from_errno("close");
> +	if (fd2 != -1 && close(fd2) == -1 && err == NULL)
> +		err = got_error_from_errno("close");
>  	return err;
>  }
>  
> @@ -3691,7 +3706,7 @@ get_changed_paths(struct got_pathlist_head *paths,
>  	if (err)
>  		goto done;
>  
> -	err = got_diff_tree(tree1, tree2, NULL, NULL, "", "", repo,
> +	err = got_diff_tree(tree1, tree2, NULL, NULL, -1, -1, "", "", repo,
>  	    got_diff_tree_collect_changed_paths, paths, 0);
>  done:
>  	if (tree1)
> @@ -4634,7 +4649,7 @@ print_diff(void *arg, unsigned char status, unsigned c
>  	struct print_diff_arg *a = arg;
>  	const struct got_error *err = NULL;
>  	struct got_blob_object *blob1 = NULL;
> -	int fd = -1, fd1 = -1;
> +	int fd = -1, fd1 = -1, fd2 = -1;
>  	FILE *f1 = NULL, *f2 = NULL;
>  	char *abspath = NULL, *label1 = NULL;
>  	struct stat sb;
> @@ -4693,7 +4708,17 @@ print_diff(void *arg, unsigned char status, unsigned c
>  			err = got_error_from_errno("got_opentemp");
>  			goto done;
>  		}
> -		err = got_diff_objects_as_blobs(NULL, NULL, f1, f2,
> +		fd1 = got_opentempfd();
> +		if (fd1 == -1) {
> +			err = got_error_from_errno("got_opentempfd");
> +			goto done;
> +		}
> +		fd2 = got_opentempfd();
> +		if (fd2 == -1) {
> +			err = got_error_from_errno("got_opentempfd");
> +			goto done;
> +		}
> +		err = got_diff_objects_as_blobs(NULL, NULL, f1, f2, fd1, fd2,
>  		    blob_id, staged_blob_id, label1, label2, a->diff_context,
>  		    a->ignore_whitespace, a->force_text_diff, a->repo, stdout);
>  		goto done;
> @@ -4794,6 +4819,8 @@ print_diff(void *arg, unsigned char status, unsigned c
>  done:
>  	if (fd1 != -1 && close(fd1) == -1 && err == NULL)
>  		err = got_error_from_errno("close");
> +	if (fd2 != -1 && close(fd2) == -1 && err == NULL)
> +		err = got_error_from_errno("close");
>  	if (blob1)
>  		got_object_blob_close(blob1);
>  	if (f1 && fclose(f1) == EOF && err == NULL)
> @@ -4825,6 +4852,7 @@ cmd_diff(int argc, char *argv[])
>  	struct got_pathlist_head paths;
>  	struct got_pathlist_entry *pe;
>  	FILE *f1 = NULL, *f2 = NULL;
> +	int fd1 = -1, fd2 = -1;
>  	int *pack_fds = NULL;
>  
>  	TAILQ_INIT(&refs);
> @@ -5134,22 +5162,34 @@ cmd_diff(int argc, char *argv[])
>  		goto done;
>  	}
>  
> +	fd1 = got_opentempfd();
> +	if (fd1 == -1) {
> +		error = got_error_from_errno("got_opentempfd");
> +		goto done;
> +	}
> +
> +	fd2 = got_opentempfd();
> +	if (fd2 == -1) {
> +		error = got_error_from_errno("got_opentempfd");
> +		goto done;
> +	}
> +
>  	switch (type1 == GOT_OBJ_TYPE_ANY ? type2 : type1) {
>  	case GOT_OBJ_TYPE_BLOB:
>  		error = got_diff_objects_as_blobs(NULL, NULL, f1, f2,
> -		    ids[0], ids[1], NULL, NULL, diff_context,
> +		    fd1, fd2, ids[0], ids[1], NULL, NULL, diff_context,
>  		    ignore_whitespace, force_text_diff, repo, stdout);
>  		break;
>  	case GOT_OBJ_TYPE_TREE:
> -		error = got_diff_objects_as_trees(NULL, NULL, f1, f2,
> +		error = got_diff_objects_as_trees(NULL, NULL, f1, f2, fd1, fd2,
>  		    ids[0], ids[1], &paths, "", "", diff_context,
>  		    ignore_whitespace, force_text_diff, repo, stdout);
>  		break;
>  	case GOT_OBJ_TYPE_COMMIT:
>  		printf("diff %s %s\n", labels[0], labels[1]);
>  		error = got_diff_objects_as_commits(NULL, NULL, f1, f2,
> -		    ids[0], ids[1], &paths, diff_context, ignore_whitespace,
> -		    force_text_diff, repo, stdout);
> +		    fd1, fd2, ids[0], ids[1], &paths, diff_context,
> +		    ignore_whitespace, force_text_diff, repo, stdout);
>  		break;
>  	default:
>  		error = got_error(GOT_ERR_OBJ_TYPE);
> @@ -5180,6 +5220,10 @@ done:
>  		error = got_error_from_errno("fclose");
>  	if (f2 && fclose(f2) == EOF && error == NULL)
>  		error = got_error_from_errno("fclose");
> +	if (fd1 != -1 && close(fd1) == -1 && error == NULL)
> +		error = got_error_from_errno("close");
> +	if (fd2 != -1 && close(fd2) == -1 && error == NULL)
> +		error = got_error_from_errno("close");
>  	return error;
>  }
>  
> @@ -9386,7 +9430,7 @@ check_path_prefix(struct got_object_id *parent_id,
>  		cpp_arg.path_prefix++;
>  	cpp_arg.len = strlen(cpp_arg.path_prefix);
>  	cpp_arg.errcode = errcode;
> -	err = got_diff_tree(tree1, tree2, NULL, NULL, "", "", repo,
> +	err = got_diff_tree(tree1, tree2, NULL, NULL, -1, -1, "", "", repo,
>  	    check_path_prefix_in_diff, &cpp_arg, 0);
>  done:
>  	if (tree1)
> blob - ffc24fe2d445b82dd5d5ff2616a3ce538f584cb8
> blob + 1bfe144b854b965c11288ba9d3508dd0576c7703
> --- gotweb/gotweb.c
> +++ gotweb/gotweb.c
> @@ -2855,6 +2855,7 @@ gw_output_diff(struct gw_trans *gw_trans, struct gw_he
>  {
>  	const struct got_error *error;
>  	FILE *f = NULL, *f1 = NULL, *f2 = NULL;
> +	int fd1 = -1, fd2 = -1;
>  	struct got_object_id *id1 = NULL, *id2 = NULL;
>  	char *label1 = NULL, *label2 = NULL, *line = NULL;
>  	int obj_type;
> @@ -2878,6 +2879,18 @@ gw_output_diff(struct gw_trans *gw_trans, struct gw_he
>  		goto done;
>  	}
>  
> +	fd1 = got_opentempfd();
> +	if (fd1 == -1) {
> +		error = got_error_from_errno("got_opentempfd");
> +		goto done;
> +	}
> +
> +	fd2 = got_opentempfd();
> +	if (fd2 == -1) {
> +		error = got_error_from_errno("got_opentempfd");
> +		goto done;
> +	}
> +
>  	if (header->parent_id != NULL &&
>  	    strncmp(header->parent_id, "/dev/null", 9) != 0) {
>  		error = got_repo_match_object_id(&id1, &label1,
> @@ -2899,15 +2912,17 @@ gw_output_diff(struct gw_trans *gw_trans, struct gw_he
>  	switch (obj_type) {
>  	case GOT_OBJ_TYPE_BLOB:
>  		error = got_diff_objects_as_blobs(NULL, NULL, f1, f2,
> -		    id1, id2, NULL, NULL, 3, 0, 0, gw_trans->repo, f);
> +		    fd1, fd2, id1, id2, NULL, NULL, 3, 0, 0,
> +		    gw_trans->repo, f);
>  		break;
>  	case GOT_OBJ_TYPE_TREE:
>  		error = got_diff_objects_as_trees(NULL, NULL, f1, f2,
> -		    id1, id2, NULL, "", "", 3, 0, 0, gw_trans->repo, f);
> +		    fd1, fd2, id1, id2, NULL, "", "", 3, 0, 0,
> +		    gw_trans->repo, f);
>  		break;
>  	case GOT_OBJ_TYPE_COMMIT:
>  		error = got_diff_objects_as_commits(NULL, NULL, f1, f2,
> -		    id1, id2, NULL, 3, 0, 0, gw_trans->repo, f);
> +		    fd1, fd2, id1, id2, NULL, 3, 0, 0, gw_trans->repo, f);
>  		break;
>  	default:
>  		error = got_error(GOT_ERR_OBJ_TYPE);
> @@ -2941,6 +2956,10 @@ done:
>  		error = got_error_from_errno("fclose");
>  	if (f2 && fclose(f2) == EOF && error == NULL)
>  		error = got_error_from_errno("fclose");
> +	if (fd1 != -1 && close(fd1) == -1 && error == NULL)
> +		error = got_error_from_errno("close");
> +	if (fd2 != -1 && close(fd2) == -1 && error == NULL)
> +		error = got_error_from_errno("close");
>  	free(line);
>  	free(label1);
>  	free(label2);
> blob - 4f537113e196997bfae7b568a9d90ab8fe9e77b0
> blob + c587513f34441a66ffd34aa5788327dc50f06c7d
> --- include/got_diff.h
> +++ include/got_diff.h
> @@ -102,14 +102,16 @@ const struct got_error *got_diff_blob_output_unidiff(v
>   * Diffing of blob content can be suppressed by passing zero for the
>   * 'diff_content' parameter. The callback will then only receive blob
>   * object IDs and diff labels, but NULL pointers instead of blob objects.
> - * If 'diff_content' is set, two open temporary files must be provided
> - * for internal use; these files can be obtained from got_opentemp()
> + * If 'diff_content' is set, two open temporary FILEs and two open
> + * temporary file descriptors must be provided for internal use; these
> + * files can be obtained from got_opentemp() and got_opentempfd(),
>   * and must be closed by the caller. Otherwise the files can be NULL.
>   * The set of arguments relating to either tree may be NULL to indicate
>   * that no content is present on its respective side of the diff.
>   */
>  const struct got_error *got_diff_tree(struct got_tree_object *,
> -    struct got_tree_object *, FILE *, FILE *, const char *, const char *,
> +    struct got_tree_object *, FILE *, FILE *, int, int,
> +    const char *, const char *,
>      struct got_repository *, got_diff_blob_cb cb, void *cb_arg, int);
>  
>  /*
> @@ -137,9 +139,10 @@ const struct got_error *got_diff_tree_collect_changed_
>   * Diff two objects, assuming both objects are blobs. Two const char * diff
>   * header labels may be provided which will be used to identify each blob in
>   * the diff output. If a label is NULL, use the blob's SHA1 checksum instead.
> - * Two open temporary files must be provided for internal use; these files
> - * can be obtained from got_opentemp() and must be closed by the caller.
> - * The set of arguments relating to either blob may be NULL to indicate
> + * Two open temporary files and two temporary file descriptors must be
> + * provided for internal use; these files can be obtained from
> + * got_opentemp() and got_opentempfd(), and must be closed by the caller.
> + * The set of arguments relating to either blob may be NULL/-1 to indicate
>   * that no content is present on its respective side of the diff.
>   * The number of context lines to show in the diff must be specified as well.
>   * Write unified diff text to the provided output FILE.
> @@ -147,7 +150,7 @@ const struct got_error *got_diff_tree_collect_changed_
>   * array of line offsets for, and the number of lines in, the unidiff text.
>   */
>  const struct got_error *got_diff_objects_as_blobs(off_t **, size_t *,
> -    FILE *, FILE *, struct got_object_id *, struct got_object_id *,
> +    FILE *, FILE *, int, int, struct got_object_id *, struct got_object_id *,
>      const char *, const char *, int, int, int,
>      struct got_repository *, FILE *);
>  
> @@ -156,8 +159,10 @@ const struct got_error *got_diff_objects_as_blobs(off_
>   * header labels may be provided which will be used to identify each blob in
>   * the trees. If a label is NULL, use the blob's SHA1 checksum instead.
>   * The number of context lines to show in diffs must be specified.
> - * Two open temporary files must be provided for internal use; these files
> - * can be obtained from got_opentemp() and must be closed by the caller.
> + * Two open temporary files and two temporary file descriptors must be
> + * provided for internal use; these files can be obtained from
> + * got_opentemp() and got_opentempfd(), and must be closed by the caller.
> + * If 'diff_content' is not set, the files may be NULL / -1.
>   * The set of arguments relating to either tree may be NULL to indicate
>   * that no content is present on its respective side of the diff.
>   * Write unified diff text to the provided output FILE.
> @@ -165,15 +170,16 @@ const struct got_error *got_diff_objects_as_blobs(off_
>   * array of line offsets for, and the number of lines in, the unidiff text.
>   */
>  const struct got_error *got_diff_objects_as_trees(off_t **, size_t *,
> -    FILE *, FILE *, struct got_object_id *, struct got_object_id *,
> +    FILE *, FILE *, int, int, struct got_object_id *, struct got_object_id *,
>      struct got_pathlist_head *, const char *, const char *, int, int, int,
>      struct got_repository *, FILE *);
>  
>  /*
>   * Diff two objects, assuming both objects are commits.
>   * The number of context lines to show in diffs must be specified.
> - * Two open temporary files must be provided for internal use; these files
> - * can be obtained from got_opentemp() and must be closed by the caller.
> + * Two open temporary files and two temporary file descriptors must be
> + * provided for internal use; these files can be obtained from
> + * got_opentemp() and got_opentempfd(), and must be closed by the caller.
>   * The set of arguments relating to either commit may be NULL to indicate
>   * that no content is present on its respective side of the diff.
>   * Write unified diff text to the provided output FILE.
> @@ -181,7 +187,7 @@ const struct got_error *got_diff_objects_as_trees(off_
>   * array of line offsets for, and the number of lines in, the unidiff text.
>   */
>  const struct got_error *got_diff_objects_as_commits(off_t **, size_t *,
> -    FILE *, FILE *, struct got_object_id *, struct got_object_id *,
> +    FILE *, FILE *, int, int, struct got_object_id *, struct got_object_id *,
>      struct got_pathlist_head *, int, int, int, struct got_repository *, FILE *);
>  
>  #define GOT_DIFF_MAX_CONTEXT	64
> blob - 7bc3ad07ee796048b5285aabebb4e367fe9de9d5
> blob + 13a1b0bf323c1c003dbb7b2473b2d1a20c202a43
> --- lib/diff.c
> +++ lib/diff.c
> @@ -273,25 +273,18 @@ got_diff_blob_file(struct got_blob_object *blob1, FILE
>  }
>  
>  static const struct got_error *
> -diff_added_blob(struct got_object_id *id, FILE *f, const char *label,
> -    mode_t mode, struct got_repository *repo,
> +diff_added_blob(struct got_object_id *id, FILE *f, int fd,
> +    const char *label, mode_t mode, struct got_repository *repo,
>      got_diff_blob_cb cb, void *cb_arg)
>  {
>  	const struct got_error *err;
>  	struct got_blob_object  *blob = NULL;
>  	struct got_object *obj = NULL;
> -	int fd = -1;
>  
>  	err = got_object_open(&obj, repo, id);
>  	if (err)
>  		return err;
>  
> -	fd = got_opentempfd();
> -	if (fd == -1) {
> -		err = got_error_from_errno("got_opentempfd");
> -		goto done;
> -	}
> -
>  	err = got_object_blob_open(&blob, repo, obj, 8192, fd);
>  	if (err)
>  		goto done;
> @@ -299,8 +292,6 @@ diff_added_blob(struct got_object_id *id, FILE *f, con
>  	    NULL, label, 0, mode, repo);
>  done:
>  	got_object_close(obj);
> -	if (fd != -1 && close(fd) == -1 && err == NULL)
> -		err = got_error_from_errno("close");
>  	if (blob)
>  		got_object_blob_close(blob);
>  	return err;
> @@ -308,7 +299,8 @@ done:
>  
>  static const struct got_error *
>  diff_modified_blob(struct got_object_id *id1, struct got_object_id *id2,
> -    FILE *f1, FILE *f2, const char *label1, const char *label2,
> +    FILE *f1, FILE *f2, int fd1, int fd2,
> +    const char *label1, const char *label2,
>      mode_t mode1, mode_t mode2, struct got_repository *repo,
>      got_diff_blob_cb cb, void *cb_arg)
>  {
> @@ -317,23 +309,11 @@ diff_modified_blob(struct got_object_id *id1, struct g
>  	struct got_object *obj2 = NULL;
>  	struct got_blob_object *blob1 = NULL;
>  	struct got_blob_object *blob2 = NULL;
> -	int fd1 = -1, fd2 = -1;
>  
>  	err = got_object_open(&obj1, repo, id1);
>  	if (err)
>  		return err;
>  
> -	fd1 = got_opentempfd();
> -	if (fd1 == -1) {
> -		err = got_error_from_errno("got_opentempfd");
> -		goto done;
> -	}
> -	fd2 = got_opentempfd();
> -	if (fd2 == -1) {
> -		err = got_error_from_errno("got_opentempfd");
> -		goto done;
> -	}
> -
>  	if (obj1->type != GOT_OBJ_TYPE_BLOB) {
>  		err = got_error(GOT_ERR_OBJ_TYPE);
>  		goto done;
> @@ -362,30 +342,22 @@ done:
>  		got_object_close(obj1);
>  	if (obj2)
>  		got_object_close(obj2);
> -	if (fd1 != -1 && close(fd1) == -1 && err == NULL)
> -		err = got_error_from_errno("close");
>  	if (blob1)
>  		got_object_blob_close(blob1);
> -	if (fd2 != -1 && close(fd2) == -1 && err == NULL)
> -		err = got_error_from_errno("close");
>  	if (blob2)
>  		got_object_blob_close(blob2);
>  	return err;
>  }
>  
>  static const struct got_error *
> -diff_deleted_blob(struct got_object_id *id, FILE *f, const char *label,
> -    mode_t mode, struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg)
> +diff_deleted_blob(struct got_object_id *id, FILE *f, int fd,
> +   const char *label, mode_t mode, struct got_repository *repo,
> +   got_diff_blob_cb cb, void *cb_arg)
>  {
>  	const struct got_error *err;
>  	struct got_blob_object  *blob = NULL;
>  	struct got_object *obj = NULL;
> -	int fd = -1;
>  
> -	fd = got_opentempfd();
> -	if (fd == -1)
> -		return got_error_from_errno("got_opentempfd");
> -
>  	err = got_object_open(&obj, repo, id);
>  	if (err)
>  		return err;
> @@ -397,15 +369,13 @@ diff_deleted_blob(struct got_object_id *id, FILE *f, c
>  	    mode, 0, repo);
>  done:
>  	got_object_close(obj);
> -	if (fd != -1 && close(fd) == -1 && err == NULL)
> -		err = got_error_from_errno("close");
>  	if (blob)
>  		got_object_blob_close(blob);
>  	return err;
>  }
>  
>  static const struct got_error *
> -diff_added_tree(struct got_object_id *id, FILE *f, const char *label,
> +diff_added_tree(struct got_object_id *id, FILE *f, int fd, const char *label,
>      struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg,
>      int diff_content)
>  {
> @@ -426,7 +396,7 @@ diff_added_tree(struct got_object_id *id, FILE *f, con
>  	if (err)
>  		goto done;
>  
> -	err = got_diff_tree(NULL, tree, NULL, f, NULL, label,
> +	err = got_diff_tree(NULL, tree, NULL, f, -1, fd, NULL, label,
>  	    repo, cb, cb_arg, diff_content);
>  done:
>  	if (tree)
> @@ -438,7 +408,8 @@ done:
>  
>  static const struct got_error *
>  diff_modified_tree(struct got_object_id *id1, struct got_object_id *id2,
> -    FILE *f1, FILE *f2, const char *label1, const char *label2,
> +    FILE *f1, FILE *f2, int fd1, int fd2,
> +    const char *label1, const char *label2,
>      struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg,
>      int diff_content)
>  {
> @@ -474,8 +445,8 @@ diff_modified_tree(struct got_object_id *id1, struct g
>  	if (err)
>  		goto done;
>  
> -	err = got_diff_tree(tree1, tree2, f1, f2, label1, label2,
> -	    repo, cb, cb_arg, diff_content);
> +	err = got_diff_tree(tree1, tree2, f1, f2, fd1, fd2,
> +	    label1, label2, repo, cb, cb_arg, diff_content);
>  
>  done:
>  	if (tree1)
> @@ -490,9 +461,9 @@ done:
>  }
>  
>  static const struct got_error *
> -diff_deleted_tree(struct got_object_id *id, FILE *f, const char *label,
> -    struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg,
> -    int diff_content)
> +diff_deleted_tree(struct got_object_id *id, FILE *f, int fd,
> +    const char *label, struct got_repository *repo,
> +    got_diff_blob_cb cb, void *cb_arg, int diff_content)
>  {
>  	const struct got_error *err;
>  	struct got_object *treeobj = NULL;
> @@ -511,7 +482,7 @@ diff_deleted_tree(struct got_object_id *id, FILE *f, c
>  	if (err)
>  		goto done;
>  
> -	err = got_diff_tree(tree, NULL, f, NULL, label, NULL,
> +	err = got_diff_tree(tree, NULL, f, NULL, fd, -1, label, NULL,
>  	    repo, cb, cb_arg, diff_content);
>  done:
>  	if (tree)
> @@ -532,7 +503,8 @@ diff_kind_mismatch(struct got_object_id *id1, struct g
>  
>  static const struct got_error *
>  diff_entry_old_new(struct got_tree_entry *te1, struct got_tree_entry *te2,
> -    FILE *f1, FILE *f2, const char *label1, const char *label2,
> +    FILE *f1, FILE *f2, int fd1, int fd2,
> +    const char *label1, const char *label2,
>      struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg,
>      int diff_content)
>  {
> @@ -544,12 +516,12 @@ diff_entry_old_new(struct got_tree_entry *te1, struct 
>  
>  	if (te2 == NULL) {
>  		if (S_ISDIR(te1->mode))
> -			err = diff_deleted_tree(&te1->id, f1, label1, repo,
> -			    cb, cb_arg, diff_content);
> +			err = diff_deleted_tree(&te1->id, f1, fd1, label1,
> +			    repo, cb, cb_arg, diff_content);
>  		else {
>  			if (diff_content)
> -				err = diff_deleted_blob(&te1->id, f1, label1,
> -				    te1->mode, repo, cb, cb_arg);
> +				err = diff_deleted_blob(&te1->id, f1, fd1,
> +				    label1, te1->mode, repo, cb, cb_arg);
>  			else
>  				err = cb(cb_arg, NULL, NULL, NULL, NULL,
>  				    &te1->id, NULL, label1, NULL,
> @@ -563,7 +535,8 @@ diff_entry_old_new(struct got_tree_entry *te1, struct 
>  	if (S_ISDIR(te1->mode) && S_ISDIR(te2->mode)) {
>  		if (!id_match)
>  			return diff_modified_tree(&te1->id, &te2->id, f1, f2,
> -			    label1, label2, repo, cb, cb_arg, diff_content);
> +			    fd1, fd2, label1, label2, repo, cb, cb_arg,
> +			    diff_content);
>  	} else if ((S_ISREG(te1->mode) || S_ISLNK(te1->mode)) &&
>  	    (S_ISREG(te2->mode) || S_ISLNK(te2->mode))) {
>  		if (!id_match ||
> @@ -571,8 +544,8 @@ diff_entry_old_new(struct got_tree_entry *te1, struct 
>  		    (te2->mode & (S_IFLNK | S_IXUSR))) {
>  			if (diff_content)
>  				return diff_modified_blob(&te1->id, &te2->id,
> -				    f1, f2, label1, label2, te1->mode,
> -				    te2->mode, repo, cb, cb_arg);
> +				    f1, f2, fd1, fd2, label1, label2,
> +				    te1->mode, te2->mode, repo, cb, cb_arg);
>  			else
>  				return cb(cb_arg, NULL, NULL, NULL, NULL,
>  				    &te1->id, &te2->id, label1, label2,
> @@ -589,7 +562,7 @@ diff_entry_old_new(struct got_tree_entry *te1, struct 
>  
>  static const struct got_error *
>  diff_entry_new_old(struct got_tree_entry *te2,
> -    struct got_tree_entry *te1, FILE *f2, const char *label2,
> +    struct got_tree_entry *te1, FILE *f2, int fd2, const char *label2,
>      struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg,
>      int diff_content)
>  {
> @@ -600,12 +573,12 @@ diff_entry_new_old(struct got_tree_entry *te2,
>  		return NULL;
>  
>  	if (S_ISDIR(te2->mode))
> -		return diff_added_tree(&te2->id, f2, label2,
> +		return diff_added_tree(&te2->id, f2, fd2, label2,
>  		    repo, cb, cb_arg, diff_content);
>  
>  	if (diff_content)
> -		return diff_added_blob(&te2->id, f2, label2, te2->mode,
> -		    repo, cb, cb_arg);
> +		return diff_added_blob(&te2->id, f2, fd2,
> +		    label2, te2->mode, repo, cb, cb_arg);
>  
>  	return cb(cb_arg, NULL, NULL, NULL, NULL, NULL, &te2->id,
>  	    NULL, label2, 0, te2->mode, repo);
> @@ -656,7 +629,8 @@ done:
>  
>  const struct got_error *
>  got_diff_tree(struct got_tree_object *tree1, struct got_tree_object *tree2,
> -    FILE *f1, FILE *f2, const char *label1, const char *label2,
> +    FILE *f1, FILE *f2, int fd1, int fd2,
> +    const char *label1, const char *label2,
>      struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg,
>      int diff_content)
>  {
> @@ -693,8 +667,8 @@ got_diff_tree(struct got_tree_object *tree1, struct go
>  					return
>  					    got_error_from_errno("asprintf");
>  			}
> -			err = diff_entry_old_new(te1, te, f1, f2, l1, l2,
> -			    repo, cb, cb_arg, diff_content);
> +			err = diff_entry_old_new(te1, te, f1, f2, fd1, fd2,
> +			    l1, l2, repo, cb, cb_arg, diff_content);
>  			if (err)
>  				break;
>  		}
> @@ -716,7 +690,7 @@ got_diff_tree(struct got_tree_object *tree1, struct go
>  					return
>  					    got_error_from_errno("asprintf");
>  			}
> -			err = diff_entry_new_old(te2, te, f2, l2, repo,
> +			err = diff_entry_new_old(te2, te, f2, fd2, l2, repo,
>  			    cb, cb_arg, diff_content);
>  			if (err)
>  				break;
> @@ -749,27 +723,18 @@ got_diff_tree(struct got_tree_object *tree1, struct go
>  
>  const struct got_error *
>  got_diff_objects_as_blobs(off_t **line_offsets, size_t *nlines,
> -    FILE *f1, FILE *f2, struct got_object_id *id1, struct got_object_id *id2,
> +    FILE *f1, FILE *f2, int fd1, int fd2,
> +    struct got_object_id *id1, struct got_object_id *id2,
>      const char *label1, const char *label2, int diff_context,
>      int ignore_whitespace, int force_text_diff,
>      struct got_repository *repo, FILE *outfile)
>  {
>  	const struct got_error *err;
>  	struct got_blob_object *blob1 = NULL, *blob2 = NULL;
> -	int fd1 = -1, fd2 = -1;
>  
>  	if (id1 == NULL && id2 == NULL)
>  		return got_error(GOT_ERR_NO_OBJ);
>  
> -	fd1 = got_opentempfd();
> -	if (fd1 == -1)
> -		return got_error_from_errno("got_opentempfd");
> -	fd2 = got_opentempfd();
> -	if (fd2 == -1) {
> -		err = got_error_from_errno("got_opentempfd");
> -		goto done;
> -	}
> -
>  	if (id1) {
>  		err = got_object_open_as_blob(&blob1, repo, id1, 8192, fd1);
>  		if (err)
> @@ -784,12 +749,8 @@ got_diff_objects_as_blobs(off_t **line_offsets, size_t
>  	    label1, label2, diff_context, ignore_whitespace, force_text_diff,
>  	    outfile);
>  done:
> -	if (fd1 != -1 && close(fd1) == -1 && err == NULL)
> -		err = got_error_from_errno("close");
>  	if (blob1)
>  		got_object_blob_close(blob1);
> -	if (fd2 != -1 && close(fd2) == -1 && err == NULL)
> -		err = got_error_from_errno("close");
>  	if (blob2)
>  		got_object_blob_close(blob2);
>  	return err;
> @@ -797,7 +758,7 @@ done:
>  
>  static const struct got_error *
>  diff_paths(struct got_tree_object *tree1, struct got_tree_object *tree2,
> -    FILE *f1, FILE *f2, struct got_pathlist_head *paths,
> +    FILE *f1, FILE *f2, int fd1, int fd2, struct got_pathlist_head *paths,
>      struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg)
>  {
>  	const struct got_error *err = NULL;
> @@ -805,16 +766,7 @@ diff_paths(struct got_tree_object *tree1, struct got_t
>  	struct got_object_id *id1 = NULL, *id2 = NULL;
>  	struct got_tree_object *subtree1 = NULL, *subtree2 = NULL;
>  	struct got_blob_object *blob1 = NULL, *blob2 = NULL;
> -	int fd1 = -1, fd2 = -1;
>  
> -	fd1 = got_opentempfd();
> -	if (fd1 == -1)
> -		return got_error_from_errno("got_opentempfd");
> -	fd2 = got_opentempfd();
> -	if (fd2 == -1) {
> -		err = got_error_from_errno("got_opentempfd");
> -		goto done;
> -	}
>  	TAILQ_FOREACH(pe, paths, entry) {
>  		int type1 = GOT_OBJ_TYPE_ANY, type2 = GOT_OBJ_TYPE_ANY;
>  		mode_t mode1 = 0, mode2 = 0;
> @@ -907,6 +859,7 @@ diff_paths(struct got_tree_object *tree1, struct got_t
>  					goto done;
>  			}
>  			err = got_diff_tree(subtree1, subtree2, f1, f2,
> +			    fd1, fd2,
>  			    id1 ? pe->path : "/dev/null",
>  			    id2 ? pe->path : "/dev/null",
>  			    repo, cb, cb_arg, 1);
> @@ -916,10 +869,6 @@ diff_paths(struct got_tree_object *tree1, struct got_t
>  			err = got_error(GOT_ERR_OBJ_TYPE);
>  			goto done;
>  		}
> -		if (ftruncate(fd1, 0L) == -1)
> -			return got_error_from_errno("ftruncate");
> -		if (ftruncate(fd2, 0L) == -1)
> -			return got_error_from_errno("ftruncate");
>  	}
>  done:
>  	free(id1);
> @@ -928,12 +877,8 @@ done:
>  		got_object_tree_close(subtree1);
>  	if (subtree2)
>  		got_object_tree_close(subtree2);
> -	if (fd1 != -1 && close(fd1) == -1 && err == NULL)
> -		err = got_error_from_errno("close");
>  	if (blob1)
>  		got_object_blob_close(blob1);
> -	if (fd2 != -1 && close(fd2) == -1 && err == NULL)
> -		err = got_error_from_errno("close");
>  	if (blob2)
>  		got_object_blob_close(blob2);
>  	return err;
> @@ -967,7 +912,8 @@ show_object_id(off_t **line_offsets, size_t *nlines, c
>  
>  static const struct got_error *
>  diff_objects_as_trees(off_t **line_offsets, size_t *nlines,
> -    FILE *f1, FILE *f2, struct got_object_id *id1, struct got_object_id *id2,
> +    FILE *f1, FILE *f2, int fd1, int fd2,
> +    struct got_object_id *id1, struct got_object_id *id2,
>      struct got_pathlist_head *paths, const char *label1, const char *label2,
>      int diff_context, int ignore_whitespace, int force_text_diff,
>      struct got_repository *repo, FILE *outfile)
> @@ -1003,10 +949,11 @@ diff_objects_as_trees(off_t **line_offsets, size_t *nl
>  		arg.nlines = 0;
>  	}
>  	if (paths == NULL || TAILQ_EMPTY(paths)) {
> -		err = got_diff_tree(tree1, tree2, f1, f2, label1, label2,
> -		    repo, got_diff_blob_output_unidiff, &arg, 1);
> +		err = got_diff_tree(tree1, tree2, f1, f2, fd1, fd2,
> +		    label1, label2, repo,
> +		    got_diff_blob_output_unidiff, &arg, 1);
>  	} else {
> -		err = diff_paths(tree1, tree2, f1, f2, paths, repo,
> +		err = diff_paths(tree1, tree2, f1, f2, fd1, fd2, paths, repo,
>  		    got_diff_blob_output_unidiff, &arg);
>  	}
>  	if (want_lineoffsets) {
> @@ -1023,7 +970,8 @@ done:
>  
>  const struct got_error *
>  got_diff_objects_as_trees(off_t **line_offsets, size_t *nlines,
> -    FILE *f1, FILE *f2, struct got_object_id *id1, struct got_object_id *id2,
> +    FILE *f1, FILE *f2, int fd1, int fd2,
> +    struct got_object_id *id1, struct got_object_id *id2,
>      struct got_pathlist_head *paths, const char *label1, const char *label2,
>      int diff_context, int ignore_whitespace, int force_text_diff,
>      struct got_repository *repo, FILE *outfile)
> @@ -1068,8 +1016,8 @@ got_diff_objects_as_trees(off_t **line_offsets, size_t
>  			goto done;
>  	}
>  
> -	err = diff_objects_as_trees(line_offsets, nlines, f1, f2, id1, id2,
> -	    paths, label1, label2, diff_context, ignore_whitespace,
> +	err = diff_objects_as_trees(line_offsets, nlines, f1, f2, fd1, fd2,
> +	    id1, id2, paths, label1, label2, diff_context, ignore_whitespace,
>  	    force_text_diff, repo, outfile);
>  done:
>  	free(idstr);
> @@ -1078,7 +1026,8 @@ done:
>  
>  const struct got_error *
>  got_diff_objects_as_commits(off_t **line_offsets, size_t *nlines,
> -    FILE *f1, FILE *f2, struct got_object_id *id1, struct got_object_id *id2,
> +    FILE *f1, FILE *f2, int fd1, int fd2,
> +    struct got_object_id *id1, struct got_object_id *id2,
>      struct got_pathlist_head *paths,
>      int diff_context, int ignore_whitespace, int force_text_diff,
>      struct got_repository *repo, FILE *outfile)
> @@ -1122,7 +1071,7 @@ got_diff_objects_as_commits(off_t **line_offsets, size
>  	if (err)
>  		goto done;
>  
> -	err = diff_objects_as_trees(line_offsets, nlines, f1, f2,
> +	err = diff_objects_as_trees(line_offsets, nlines, f1, f2, fd1, fd2,
>  	    commit1 ? got_object_commit_get_tree_id(commit1) : NULL,
>  	    got_object_commit_get_tree_id(commit2), paths, "", "",
>  	    diff_context, ignore_whitespace, force_text_diff, repo, outfile);
> blob - 8decec3748ea2448a4255cba6eccf77ced9fab2a
> blob + 3ffd653ef429fab490d06ba6a953185254e7c117
> --- lib/error.c
> +++ lib/error.c
> @@ -236,6 +236,7 @@ got_error(int code)
>  {
>  	size_t i;
>  
> +	if (code == GOT_ERR_PRIVSEP_LEN) abort();
>  	for (i = 0; i < nitems(got_errors); i++) {
>  		if (code == got_errors[i].code)
>  			return &got_errors[i];
> blob - d2ba8bff2999366cb470b0e5b15c07f8d2a96442
> blob + 12ae8e569a5cb16540cc4d70ad34d56c0964258a
> --- lib/object.c
> +++ lib/object.c
> @@ -1368,6 +1368,15 @@ open_blob(struct got_blob_object **blob, struct got_re
>  		goto done;
>  	}
>  
> +	if (ftruncate(outfd, 0L) == -1) {
> +		err = got_error_from_errno("ftruncate");
> +		goto done;
> +	}
> +	if (lseek(outfd, SEEK_SET, 0) == -1) {
> +		err = got_error_from_errno("lseek");
> +		goto done;
> +	}
> +
>  	err = got_repo_search_packidx(&packidx, &idx, repo, id);
>  	if (err == NULL) {
>  		struct got_pack *pack = NULL;
> blob - bde5fed8d73d8b12076780fe47b0782a4fdb69db
> blob + 18f13a154b509692bfadad8d3d5873d52e5bd66b
> --- lib/worktree.c
> +++ lib/worktree.c
> @@ -3136,6 +3136,7 @@ merge_files(struct got_worktree *worktree, struct got_
>  	struct merge_file_cb_arg arg;
>  	char *label_orig = NULL;
>  	FILE *f1 = NULL, *f2 = NULL;
> +	int fd1 = -1, fd2 = -1;
>  
>  	if (commit_id1) {
>  		err = got_object_open_as_commit(&commit1, repo, commit_id1);
> @@ -3191,10 +3192,22 @@ merge_files(struct got_worktree *worktree, struct got_
>  		goto done;
>  	}
>  
> +	fd1 = got_opentempfd();
> +	if (fd1 == -1) {
> +		err = got_error_from_errno("got_opentempfd");
> +		goto done;
> +	}
> +
> +	fd2 = got_opentempfd();
> +	if (fd2 == -1) {
> +		err = got_error_from_errno("got_opentempfd");
> +		goto done;
> +	}
> +
>  	cmc_arg.worktree = worktree;
>  	cmc_arg.fileindex = fileindex;
>  	cmc_arg.repo = repo;
> -	err = got_diff_tree(tree1, tree2, f1, f2, "", "", repo,
> +	err = got_diff_tree(tree1, tree2, f1, f2, fd1, fd2, "", "", repo,
>  	    check_merge_conflicts, &cmc_arg, 0);
>  	if (err)
>  		goto done;
> @@ -3208,7 +3221,7 @@ merge_files(struct got_worktree *worktree, struct got_
>  	arg.label_orig = label_orig;
>  	arg.commit_id2 = commit_id2;
>  	arg.allow_bad_symlinks = 1; /* preserve bad symlinks across merges */
> -	err = got_diff_tree(tree1, tree2, f1, f2, "", "", repo,
> +	err = got_diff_tree(tree1, tree2, f1, f2, fd1, fd2, "", "", repo,
>  	    merge_file_cb, &arg, 1);
>  	sync_err = sync_fileindex(fileindex, fileindex_path);
>  	if (sync_err && err == NULL)
> @@ -3226,6 +3239,10 @@ done:
>  		err = got_error_from_errno("fclose");
>  	if (f2 && fclose(f2) == EOF && err == NULL)
>  		err = got_error_from_errno("fclose");
> +	if (fd1 != -1 && close(fd1) == -1 && err == NULL)
> +		err = got_error_from_errno("close");
> +	if (fd2 != -1 && close(fd2) == -1 && err == NULL)
> +		err = got_error_from_errno("close");
>  	free(label_orig);
>  	return err;
>  }
> blob - 02357b5d2aa6509aa4c4c341f9bc46ba93cf213d
> blob + 609e8f12ee4e83af2f66a84f1b0ad9b823991113
> --- tog/tog.c
> +++ tog/tog.c
> @@ -309,6 +309,7 @@ struct tog_diff_view_state {
>  	struct got_object_id *id1, *id2;
>  	const char *label1, *label2;
>  	FILE *f, *f1, *f2;
> +	int fd1, fd2;
>  	int first_displayed_line;
>  	int last_displayed_line;
>  	int eof;
> @@ -3504,7 +3505,7 @@ get_changed_paths(struct got_pathlist_head *paths,
>  	if (err)
>  		goto done;
>  
> -	err = got_diff_tree(tree1, tree2, NULL, NULL, "", "", repo,
> +	err = got_diff_tree(tree1, tree2, NULL, NULL, -1, -1, "", "", repo,
>  	    got_diff_tree_collect_changed_paths, paths, 0);
>  done:
>  	if (tree1)
> @@ -3724,14 +3725,15 @@ create_diff(struct tog_diff_view_state *s)
>  	switch (obj_type) {
>  	case GOT_OBJ_TYPE_BLOB:
>  		err = got_diff_objects_as_blobs(&s->line_offsets, &s->nlines,
> -		    s->f1, s->f2, s->id1, s->id2, s->label1, s->label2,
> -		    s->diff_context, s->ignore_whitespace, s->force_text_diff,
> -		    s->repo, s->f);
> +		    s->f1, s->f2, s->fd1, s->fd2, s->id1, s->id2,
> +		    s->label1, s->label2, s->diff_context,
> +		    s->ignore_whitespace, s->force_text_diff, s->repo, s->f);
>  		break;
>  	case GOT_OBJ_TYPE_TREE:
>  		err = got_diff_objects_as_trees(&s->line_offsets, &s->nlines,
> -		    s->f1, s->f2, s->id1, s->id2, NULL, "", "", s->diff_context,
> -		    s->ignore_whitespace, s->force_text_diff, s->repo, s->f);
> +		    s->f1, s->f2, s->fd1, s->fd2, s->id1, s->id2, NULL, "", "",
> +		    s->diff_context, s->ignore_whitespace, s->force_text_diff,
> +		    s->repo, s->f);
>  		break;
>  	case GOT_OBJ_TYPE_COMMIT: {
>  		const struct got_object_id_queue *parent_ids;
> @@ -3765,8 +3767,9 @@ create_diff(struct tog_diff_view_state *s)
>  		got_object_commit_close(commit2);
>  
>  		err = got_diff_objects_as_commits(&s->line_offsets, &s->nlines,
> -		    s->f1, s->f2, s->id1, s->id2, NULL, s->diff_context,
> -		    s->ignore_whitespace, s->force_text_diff, s->repo, s->f);
> +		    s->f1, s->f2, s->fd1, s->fd2, s->id1, s->id2, NULL,
> +		    s->diff_context, s->ignore_whitespace, s->force_text_diff,
> +		    s->repo, s->f);
>  		break;
>  	}
>  	default:
> @@ -3884,12 +3887,18 @@ close_diff_view(struct tog_view *view)
>  	if (s->f && fclose(s->f) == EOF)
>  		err = got_error_from_errno("fclose");
>  	s->f = NULL;
> -	if (s->f1 && fclose(s->f1) == EOF)
> +	if (s->f1 && fclose(s->f1) == EOF && err == NULL)
>  		err = got_error_from_errno("fclose");
>  	s->f1 = NULL;
> -	if (s->f2 && fclose(s->f2) == EOF)
> +	if (s->f2 && fclose(s->f2) == EOF && err == NULL)
>  		err = got_error_from_errno("fclose");
>  	s->f2 = NULL;
> +	if (s->fd1 != -1 && close(s->fd1) == -1 && err == NULL)
> +		err = got_error_from_errno("close");
> +	s->fd1 = -1;
> +	if (s->fd2 != -1 && close(s->fd2) == -1 && err == NULL)
> +		err = got_error_from_errno("close");
> +	s->fd2 = -1;
>  	free_colors(&s->colors);
>  	free(s->line_offsets);
>  	s->line_offsets = NULL;
> @@ -3907,6 +3916,8 @@ open_diff_view(struct tog_view *view, struct got_objec
>  	struct tog_diff_view_state *s = &view->state.diff;
>  
>  	memset(s, 0, sizeof(*s));
> +	s->fd1 = -1;
> +	s->fd2 = -1;
>  
>  	if (id1 != NULL && id2 != NULL) {
>  	    int type1, type2;
> @@ -3954,6 +3965,18 @@ open_diff_view(struct tog_view *view, struct got_objec
>  		goto done;
>  	}
>  
> +	s->fd1 = got_opentempfd();
> +	if (s->fd1 == -1) {
> +		err = got_error_from_errno("got_opentempfd");
> +		goto done;
> +	}
> +
> +	s->fd2 = got_opentempfd();
> +	if (s->fd2 == -1) {
> +		err = got_error_from_errno("got_opentempfd");
> +		goto done;
> +	}
> +
>  	s->first_displayed_line = 1;
>  	s->last_displayed_line = view->nlines;
>  	s->diff_context = diff_context;
> 

-- 

Tracey Emery