From d1cc1ef6c31dc193adcf48a9aea4a95830c7ae56 Mon Sep 17 00:00:00 2001
From: Dan McGee <dan@archlinux.org>
Date: Tue, 15 Feb 2011 16:56:44 -0600
Subject: Fix some database size estimation problems

* Use stat() and not lstat(); we don't care for the size of the symlink if
  it is one, we want the size of the reference file.
* FS#22896, fix local database estimation on platforms that don't abide by
  the nlink assumption for number of children.
* Fix a missing newline on an error message.

Signed-off-by: Dan McGee <dan@archlinux.org>
---
 lib/libalpm/be_local.c | 32 ++++++++++++++++++++++++++------
 lib/libalpm/be_sync.c  |  9 +++++----
 lib/libalpm/pkghash.c  |  2 +-
 3 files changed, 32 insertions(+), 11 deletions(-)

(limited to 'lib')

diff --git a/lib/libalpm/be_local.c b/lib/libalpm/be_local.c
index 58582705..c1e86a63 100644
--- a/lib/libalpm/be_local.c
+++ b/lib/libalpm/be_local.c
@@ -367,7 +367,8 @@ static int is_dir(const char *path, struct dirent *entry)
 
 static int local_db_populate(pmdb_t *db)
 {
-	int est_count, count = 0;
+	size_t est_count;
+	int count = 0;
 	struct stat buf;
 	struct dirent *ent = NULL;
 	const char *dbpath;
@@ -379,17 +380,36 @@ static int local_db_populate(pmdb_t *db)
 
 	dbpath = _alpm_db_path(db);
 	if(dbpath == NULL) {
-		return(-1);
+		RET_ERR(PM_ERR_DB_OPEN, -1);
 	}
 	dbdir = opendir(dbpath);
 	if(dbdir == NULL) {
-		return(0);
+		if(errno == ENOENT) {
+			/* no database existing yet is not an error */
+			return(0);
+		}
+		RET_ERR(PM_ERR_DB_OPEN, -1);
 	}
 	if(fstat(dirfd(dbdir), &buf) != 0) {
-		return(0);
+		RET_ERR(PM_ERR_DB_OPEN, -1);
+	}
+	if(buf.st_nlink >= 2) {
+		est_count = buf.st_nlink;
+	} else {
+		/* Some filesystems don't subscribe to the two-implicit links school of
+		 * thought, e.g. BTRFS, HFS+. See
+		 * http://kerneltrap.org/mailarchive/linux-btrfs/2010/1/23/6723483/thread
+		 */
+		est_count = 0;
+		while((ent = readdir(dbdir)) != NULL) {
+			est_count++;
+		}
+		rewinddir(dbdir);
+	}
+	if(est_count >= 2) {
+		/* subtract the two extra pointers to get # of children */
+		est_count -= 2;
 	}
-	/* subtract the two always-there pointers to get # of children */
-	est_count = (int)buf.st_nlink - 2;
 
 	/* initialize hash at 50% full */
 	db->pkgcache = _alpm_pkghash_create(est_count * 2);
diff --git a/lib/libalpm/be_sync.c b/lib/libalpm/be_sync.c
index 4e9b4d31..69f7663d 100644
--- a/lib/libalpm/be_sync.c
+++ b/lib/libalpm/be_sync.c
@@ -171,7 +171,7 @@ static int sync_db_read(pmdb_t *db, struct archive *archive,
  * Unweighted Avg   2543.39  118.74  190.16  137.93
  * Average of Avgs  2564.44  124.08  191.06  143.46
  */
-static int estimate_package_count(struct stat *st, struct archive *archive)
+static size_t estimate_package_count(struct stat *st, struct archive *archive)
 {
 	unsigned int per_package;
 
@@ -199,12 +199,13 @@ static int estimate_package_count(struct stat *st, struct archive *archive)
 			/* assume it is at least somewhat compressed */
 			per_package = 200;
 	}
-	return((int)(st->st_size / per_package) + 1);
+	return((size_t)(st->st_size / per_package) + 1);
 }
 
 static int sync_db_populate(pmdb_t *db)
 {
-	int est_count, count = 0;
+	size_t est_count;
+	int count = 0;
 	struct stat buf;
 	struct archive *archive;
 	struct archive_entry *entry;
@@ -227,7 +228,7 @@ static int sync_db_populate(pmdb_t *db)
 		archive_read_finish(archive);
 		RET_ERR(PM_ERR_DB_OPEN, 1);
 	}
-	if(lstat(_alpm_db_path(db), &buf) != 0) {
+	if(stat(_alpm_db_path(db), &buf) != 0) {
 		RET_ERR(PM_ERR_DB_OPEN, 1);
 	}
 	est_count = estimate_package_count(&buf, archive);
diff --git a/lib/libalpm/pkghash.c b/lib/libalpm/pkghash.c
index 23b7493a..db98f94b 100644
--- a/lib/libalpm/pkghash.c
+++ b/lib/libalpm/pkghash.c
@@ -70,7 +70,7 @@ pmpkghash_t *_alpm_pkghash_create(size_t size)
 	}
 
 	if(hash->buckets < size) {
-		_alpm_log(PM_LOG_ERROR, _("database larger than maximum size"));
+		_alpm_log(PM_LOG_ERROR, _("database larger than maximum size\n"));
 		free(hash);
 		return(NULL);
 	}
-- 
cgit v1.2.3-70-g09d2