Commit Diff


commit - 3991b2a5fc4bf9e028e24e4ea4f7f66f1ed4ff6c
commit + e0220e748cf7158ec0ccebeb968ca59fe730c162
blob - 21cef21a957f01b6963070f9d14fcec19d630204
blob + b4de5f2012019833ba703e5f3ccfee084c611496
--- lib/repository.c
+++ lib/repository.c
@@ -263,16 +263,16 @@ close_tempfiles(int *fds, size_t nfds)
 }
 
 static const struct got_error *
-open_tempfiles(int **fds, size_t nfds)
+open_tempfiles(int **fds, size_t array_size, size_t nfds)
 {
 	const struct got_error *err = NULL;
 	int i;
 
-	*fds = calloc(nfds, sizeof(**fds));
+	*fds = calloc(array_size, sizeof(**fds));
 	if (*fds == NULL)
 		return got_error_from_errno("calloc");
 
-	for (i = 0; i < nfds; i++)
+	for (i = 0; i < array_size; i++)
 		(*fds)[i] = -1;
 
 	for (i = 0; i < nfds; i++) {
@@ -288,10 +288,40 @@ open_tempfiles(int **fds, size_t nfds)
 	return NULL;
 }
 
+static const struct got_error *
+get_pack_cache_size(int *pack_cache_size)
+{
+	struct rlimit rl;
+
+	if (getrlimit(RLIMIT_NOFILE, &rl) == -1)
+		return got_error_from_errno("getrlimit");
+
+	*pack_cache_size = GOT_PACK_CACHE_SIZE;
+	if (*pack_cache_size > rl.rlim_cur / 8)
+		*pack_cache_size = rl.rlim_cur / 8;
+
+	return NULL;
+}
+
 const struct got_error *
 got_repo_pack_fds_open(int **pack_fds)
 {
-	return open_tempfiles(pack_fds, GOT_PACK_NUM_TEMPFILES);
+	const struct got_error *err;
+	int nfds;
+
+	err = get_pack_cache_size(&nfds);
+	if (err)
+		return err;
+
+	/*
+	 * We need one basefd and one accumfd per cached pack.
+	 * Our constants should be set up in a way such that
+	 * this error never triggers.
+	 */
+	if (nfds * 2 > GOT_PACK_NUM_TEMPFILES)
+		return got_error(GOT_ERR_NO_SPACE);
+
+	return open_tempfiles(pack_fds, GOT_PACK_NUM_TEMPFILES, nfds * 2);
 }
 
 const struct got_error *
@@ -303,7 +333,8 @@ got_repo_pack_fds_close(int *pack_fds)
 const struct got_error *
 got_repo_temp_fds_open(int **temp_fds) 
 {
-	return open_tempfiles(temp_fds, GOT_REPO_NUM_TEMPFILES);
+	return open_tempfiles(temp_fds, GOT_REPO_NUM_TEMPFILES,
+	    GOT_REPO_NUM_TEMPFILES);
 }
 
 void
@@ -633,12 +664,8 @@ got_repo_open(struct got_repository **repop, const cha
 	const struct got_error *err = NULL;
 	char *repo_path = NULL;
 	size_t i, j = 0;
-	struct rlimit rl;
 
 	*repop = NULL;
-
-	if (getrlimit(RLIMIT_NOFILE, &rl) == -1)
-		return got_error_from_errno("getrlimit");
 
 	repo = calloc(1, sizeof(*repo));
 	if (repo == NULL)
@@ -674,9 +701,9 @@ got_repo_open(struct got_repository **repop, const cha
 	if (err)
 		goto done;
 
-	repo->pack_cache_size = GOT_PACK_CACHE_SIZE;
-	if (repo->pack_cache_size > rl.rlim_cur / 8)
-		repo->pack_cache_size = rl.rlim_cur / 8;
+	err = get_pack_cache_size(&repo->pack_cache_size);
+	if (err)
+		goto done;
 	for (i = 0; i < nitems(repo->packs); i++) {
 		if (pack_fds != NULL && i < repo->pack_cache_size) {
 			repo->packs[i].basefd = pack_fds[j++];
blob - 3ceb6774f7bce8a411b960566268aea4c45554cc
blob + fa93789a82f9ac4e822dda641bca1e8841f33829
--- regress/cmdline/checkout.sh
+++ regress/cmdline/checkout.sh
@@ -887,8 +887,53 @@ test_checkout_umask() {
 	done
 
 	test_done "$testroot" 0
+}
+
+test_checkout_ulimit_n() {
+	local testroot=`test_init checkout_ulimit_n`
+
+	echo -n "Checked out refs/heads/master: " >> $testroot/stdout.expected
+	git_show_head $testroot/repo >> $testroot/stdout.expected
+	printf "\nNow shut up and hack\n" >> $testroot/stdout.expected
+
+	# Drastically reduce the number of files we are allowed to use.
+	# This tests our down-scaling of caches which store open file handles.
+	# Checkout should still work; if it does not, then either there is
+	# a bug or the fixed limit used by this test case is no longer valid
+	# and must be raised.
+	ulimit -n 20
+
+	got checkout -q $testroot/repo $testroot/wt > $testroot/stdout
+	ret=$?
+	if [ $ret -ne 0 ]; then
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	cmp -s $testroot/stdout.expected $testroot/stdout
+	ret=$?
+	if [ $ret -ne 0 ]; then
+		diff -u $testroot/stdout.expected $testroot/stdout
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	echo "alpha" > $testroot/content.expected
+	echo "beta" >> $testroot/content.expected
+	echo "zeta" >> $testroot/content.expected
+	echo "delta" >> $testroot/content.expected
+	cat $testroot/wt/alpha $testroot/wt/beta $testroot/wt/epsilon/zeta \
+	    $testroot/wt/gamma/delta > $testroot/content
+
+	cmp -s $testroot/content.expected $testroot/content
+	ret=$?
+	if [ $ret -ne 0 ]; then
+		diff -u $testroot/content.expected $testroot/content
+	fi
+	test_done "$testroot" "$ret"
 }
 
+
 test_parseargs "$@"
 run_test test_checkout_basic
 run_test test_checkout_dir_exists
@@ -904,3 +949,4 @@ run_test test_checkout_symlink_relative_wtpath
 run_test test_checkout_repo_with_unknown_extension
 run_test test_checkout_quiet
 run_test test_checkout_umask
+run_test test_checkout_ulimit_n