1 /*
2  *  be_local.c : backend for the local database
3  *
4  *  Copyright (c) 2006-2018 Pacman Development Team <pacman-dev@archlinux.org>
5  *  Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org>
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include <unistd.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <stdint.h> /* intmax_t */
27 #include <sys/stat.h>
28 #include <dirent.h>
29 #include <limits.h> /* PATH_MAX */
30 
31 /* libarchive */
32 #include <archive.h>
33 #include <archive_entry.h>
34 
35 /* libalpm */
36 #include "db.h"
37 #include "alpm_list.h"
38 #include "libarchive-compat.h"
39 #include "log.h"
40 #include "util.h"
41 #include "alpm.h"
42 #include "handle.h"
43 #include "package.h"
44 #include "deps.h"
45 #include "filelist.h"
46 
47 /* local database format version */
48 size_t ALPM_LOCAL_DB_VERSION = 9;
49 
50 static int local_db_read(alpm_pkg_t *info, int inforeq);
51 
52 #define LAZY_LOAD(info) \
53 	do { \
54 		if(!(pkg->infolevel & info)) { \
55 			local_db_read(pkg, info); \
56 		} \
57 	} while(0)
58 
59 
60 /* Cache-specific accessor functions. These implementations allow for lazy
61  * loading by the files backend when a data member is actually needed
62  * rather than loading all pieces of information when the package is first
63  * initialized.
64  */
65 
_cache_get_base(alpm_pkg_t * pkg)66 static const char *_cache_get_base(alpm_pkg_t *pkg)
67 {
68 	LAZY_LOAD(INFRQ_DESC);
69 	return pkg->base;
70 }
71 
_cache_get_desc(alpm_pkg_t * pkg)72 static const char *_cache_get_desc(alpm_pkg_t *pkg)
73 {
74 	LAZY_LOAD(INFRQ_DESC);
75 	return pkg->desc;
76 }
77 
_cache_get_url(alpm_pkg_t * pkg)78 static const char *_cache_get_url(alpm_pkg_t *pkg)
79 {
80 	LAZY_LOAD(INFRQ_DESC);
81 	return pkg->url;
82 }
83 
_cache_get_builddate(alpm_pkg_t * pkg)84 static alpm_time_t _cache_get_builddate(alpm_pkg_t *pkg)
85 {
86 	LAZY_LOAD(INFRQ_DESC);
87 	return pkg->builddate;
88 }
89 
_cache_get_installdate(alpm_pkg_t * pkg)90 static alpm_time_t _cache_get_installdate(alpm_pkg_t *pkg)
91 {
92 	LAZY_LOAD(INFRQ_DESC);
93 	return pkg->installdate;
94 }
95 
_cache_get_packager(alpm_pkg_t * pkg)96 static const char *_cache_get_packager(alpm_pkg_t *pkg)
97 {
98 	LAZY_LOAD(INFRQ_DESC);
99 	return pkg->packager;
100 }
101 
_cache_get_arch(alpm_pkg_t * pkg)102 static const char *_cache_get_arch(alpm_pkg_t *pkg)
103 {
104 	LAZY_LOAD(INFRQ_DESC);
105 	return pkg->arch;
106 }
107 
_cache_get_isize(alpm_pkg_t * pkg)108 static off_t _cache_get_isize(alpm_pkg_t *pkg)
109 {
110 	LAZY_LOAD(INFRQ_DESC);
111 	return pkg->isize;
112 }
113 
_cache_get_reason(alpm_pkg_t * pkg)114 static alpm_pkgreason_t _cache_get_reason(alpm_pkg_t *pkg)
115 {
116 	LAZY_LOAD(INFRQ_DESC);
117 	return pkg->reason;
118 }
119 
_cache_get_validation(alpm_pkg_t * pkg)120 static int _cache_get_validation(alpm_pkg_t *pkg)
121 {
122 	LAZY_LOAD(INFRQ_DESC);
123 	return pkg->validation;
124 }
125 
_cache_get_licenses(alpm_pkg_t * pkg)126 static alpm_list_t *_cache_get_licenses(alpm_pkg_t *pkg)
127 {
128 	LAZY_LOAD(INFRQ_DESC);
129 	return pkg->licenses;
130 }
131 
_cache_get_groups(alpm_pkg_t * pkg)132 static alpm_list_t *_cache_get_groups(alpm_pkg_t *pkg)
133 {
134 	LAZY_LOAD(INFRQ_DESC);
135 	return pkg->groups;
136 }
137 
_cache_has_scriptlet(alpm_pkg_t * pkg)138 static int _cache_has_scriptlet(alpm_pkg_t *pkg)
139 {
140 	LAZY_LOAD(INFRQ_SCRIPTLET);
141 	return pkg->scriptlet;
142 }
143 
_cache_get_depends(alpm_pkg_t * pkg)144 static alpm_list_t *_cache_get_depends(alpm_pkg_t *pkg)
145 {
146 	LAZY_LOAD(INFRQ_DESC);
147 	return pkg->depends;
148 }
149 
_cache_get_optdepends(alpm_pkg_t * pkg)150 static alpm_list_t *_cache_get_optdepends(alpm_pkg_t *pkg)
151 {
152 	LAZY_LOAD(INFRQ_DESC);
153 	return pkg->optdepends;
154 }
155 
_cache_get_conflicts(alpm_pkg_t * pkg)156 static alpm_list_t *_cache_get_conflicts(alpm_pkg_t *pkg)
157 {
158 	LAZY_LOAD(INFRQ_DESC);
159 	return pkg->conflicts;
160 }
161 
_cache_get_provides(alpm_pkg_t * pkg)162 static alpm_list_t *_cache_get_provides(alpm_pkg_t *pkg)
163 {
164 	LAZY_LOAD(INFRQ_DESC);
165 	return pkg->provides;
166 }
167 
_cache_get_replaces(alpm_pkg_t * pkg)168 static alpm_list_t *_cache_get_replaces(alpm_pkg_t *pkg)
169 {
170 	LAZY_LOAD(INFRQ_DESC);
171 	return pkg->replaces;
172 }
173 
_cache_get_files(alpm_pkg_t * pkg)174 static alpm_filelist_t *_cache_get_files(alpm_pkg_t *pkg)
175 {
176 	LAZY_LOAD(INFRQ_FILES);
177 	return &(pkg->files);
178 }
179 
_cache_get_backup(alpm_pkg_t * pkg)180 static alpm_list_t *_cache_get_backup(alpm_pkg_t *pkg)
181 {
182 	LAZY_LOAD(INFRQ_FILES);
183 	return pkg->backup;
184 }
185 
186 /**
187  * Open a package changelog for reading. Similar to fopen in functionality,
188  * except that the returned 'file stream' is from the database.
189  * @param pkg the package (from db) to read the changelog
190  * @return a 'file stream' to the package changelog
191  */
_cache_changelog_open(alpm_pkg_t * pkg)192 static void *_cache_changelog_open(alpm_pkg_t *pkg)
193 {
194 	alpm_db_t *db = alpm_pkg_get_db(pkg);
195 	char *clfile = _alpm_local_db_pkgpath(db, pkg, "changelog");
196 	FILE *f = fopen(clfile, "r");
197 	free(clfile);
198 	return f;
199 }
200 
201 /**
202  * Read data from an open changelog 'file stream'. Similar to fread in
203  * functionality, this function takes a buffer and amount of data to read.
204  * @param ptr a buffer to fill with raw changelog data
205  * @param size the size of the buffer
206  * @param pkg the package that the changelog is being read from
207  * @param fp a 'file stream' to the package changelog
208  * @return the number of characters read, or 0 if there is no more data
209  */
_cache_changelog_read(void * ptr,size_t size,const alpm_pkg_t UNUSED * pkg,void * fp)210 static size_t _cache_changelog_read(void *ptr, size_t size,
211 		const alpm_pkg_t UNUSED *pkg, void *fp)
212 {
213 	return fread(ptr, 1, size, (FILE *)fp);
214 }
215 
216 /**
217  * Close a package changelog for reading. Similar to fclose in functionality,
218  * except that the 'file stream' is from the database.
219  * @param pkg the package that the changelog was read from
220  * @param fp a 'file stream' to the package changelog
221  * @return whether closing the package changelog stream was successful
222  */
_cache_changelog_close(const alpm_pkg_t UNUSED * pkg,void * fp)223 static int _cache_changelog_close(const alpm_pkg_t UNUSED *pkg, void *fp)
224 {
225 	return fclose((FILE *)fp);
226 }
227 
228 /**
229  * Open a package mtree file for reading.
230  * @param pkg the local package to read the changelog of
231  * @return a archive structure for the package mtree file
232  */
_cache_mtree_open(alpm_pkg_t * pkg)233 static struct archive *_cache_mtree_open(alpm_pkg_t *pkg)
234 {
235 	int r;
236 	struct archive *mtree;
237 
238 	alpm_db_t *db = alpm_pkg_get_db(pkg);
239 	char *mtfile = _alpm_local_db_pkgpath(db, pkg, "mtree");
240 
241 	if(access(mtfile, F_OK) != 0) {
242 		/* there is no mtree file for this package */
243 		goto error;
244 	}
245 
246 	if((mtree = archive_read_new()) == NULL) {
247 		pkg->handle->pm_errno = ALPM_ERR_LIBARCHIVE;
248 		goto error;
249 	}
250 
251 	_alpm_archive_read_support_filter_all(mtree);
252 	archive_read_support_format_mtree(mtree);
253 
254 	if((r = _alpm_archive_read_open_file(mtree, mtfile, ALPM_BUFFER_SIZE))) {
255 		_alpm_log(pkg->handle, ALPM_LOG_ERROR, _("error while reading file %s: %s\n"),
256 					mtfile, archive_error_string(mtree));
257 		pkg->handle->pm_errno = ALPM_ERR_LIBARCHIVE;
258 		_alpm_archive_read_free(mtree);
259 		goto error;
260 	}
261 
262 	free(mtfile);
263 	return mtree;
264 
265 error:
266 	free(mtfile);
267 	return NULL;
268 }
269 
270 /**
271  * Read next entry from a package mtree file.
272  * @param pkg the package that the mtree file is being read from
273  * @param archive the archive structure reading from the mtree file
274  * @param entry an archive_entry to store the entry header information
275  * @return 0 if end of archive is reached, non-zero otherwise.
276  */
_cache_mtree_next(const alpm_pkg_t UNUSED * pkg,struct archive * mtree,struct archive_entry ** entry)277 static int _cache_mtree_next(const alpm_pkg_t UNUSED *pkg,
278 		struct archive *mtree, struct archive_entry **entry)
279 {
280 	return archive_read_next_header(mtree, entry);
281 }
282 
283 /**
284  * Close a package mtree file for reading.
285  * @param pkg the package that the mtree file was read from
286  * @param mtree the archive structure use for reading from the mtree file
287  * @return whether closing the package changelog stream was successful
288  */
_cache_mtree_close(const alpm_pkg_t UNUSED * pkg,struct archive * mtree)289 static int _cache_mtree_close(const alpm_pkg_t UNUSED *pkg,
290 		struct archive *mtree)
291 {
292 	return _alpm_archive_read_free(mtree);
293 }
294 
_cache_force_load(alpm_pkg_t * pkg)295 static int _cache_force_load(alpm_pkg_t *pkg)
296 {
297 	return local_db_read(pkg, INFRQ_ALL);
298 }
299 
300 
301 /** The local database operations struct. Get package fields through
302  * lazy accessor methods that handle any backend loading and caching
303  * logic.
304  */
305 static struct pkg_operations local_pkg_ops = {
306 	.get_base        = _cache_get_base,
307 	.get_desc        = _cache_get_desc,
308 	.get_url         = _cache_get_url,
309 	.get_builddate   = _cache_get_builddate,
310 	.get_installdate = _cache_get_installdate,
311 	.get_packager    = _cache_get_packager,
312 	.get_arch        = _cache_get_arch,
313 	.get_isize       = _cache_get_isize,
314 	.get_reason      = _cache_get_reason,
315 	.get_validation  = _cache_get_validation,
316 	.has_scriptlet   = _cache_has_scriptlet,
317 	.get_licenses    = _cache_get_licenses,
318 	.get_groups      = _cache_get_groups,
319 	.get_depends     = _cache_get_depends,
320 	.get_optdepends  = _cache_get_optdepends,
321 	.get_conflicts   = _cache_get_conflicts,
322 	.get_provides    = _cache_get_provides,
323 	.get_replaces    = _cache_get_replaces,
324 	.get_files       = _cache_get_files,
325 	.get_backup      = _cache_get_backup,
326 
327 	.changelog_open  = _cache_changelog_open,
328 	.changelog_read  = _cache_changelog_read,
329 	.changelog_close = _cache_changelog_close,
330 
331 	.mtree_open      = _cache_mtree_open,
332 	.mtree_next      = _cache_mtree_next,
333 	.mtree_close     = _cache_mtree_close,
334 
335 	.force_load      = _cache_force_load,
336 };
337 
checkdbdir(alpm_db_t * db)338 static int checkdbdir(alpm_db_t *db)
339 {
340 	struct stat buf;
341 	const char *path = _alpm_db_path(db);
342 
343 	if(stat(path, &buf) != 0) {
344 		_alpm_log(db->handle, ALPM_LOG_DEBUG, "database dir '%s' does not exist, creating it\n",
345 				path);
346 		if(_alpm_makepath(path) != 0) {
347 			RET_ERR(db->handle, ALPM_ERR_SYSTEM, -1);
348 		}
349 	} else if(!S_ISDIR(buf.st_mode)) {
350 		_alpm_log(db->handle, ALPM_LOG_WARNING, _("removing invalid database: %s\n"), path);
351 		if(unlink(path) != 0 || _alpm_makepath(path) != 0) {
352 			RET_ERR(db->handle, ALPM_ERR_SYSTEM, -1);
353 		}
354 	}
355 	return 0;
356 }
357 
is_dir(const char * path,struct dirent * entry)358 static int is_dir(const char *path, struct dirent *entry)
359 {
360 #ifdef HAVE_STRUCT_DIRENT_D_TYPE
361 	if(entry->d_type != DT_UNKNOWN) {
362 		return (entry->d_type == DT_DIR);
363 	}
364 #endif
365 	{
366 		char buffer[PATH_MAX];
367 		struct stat sbuf;
368 
369 		snprintf(buffer, PATH_MAX, "%s/%s", path, entry->d_name);
370 
371 		if(!stat(buffer, &sbuf)) {
372 			return S_ISDIR(sbuf.st_mode);
373 		}
374 	}
375 
376 	return 0;
377 }
378 
local_db_add_version(alpm_db_t UNUSED * db,const char * dbpath)379 static int local_db_add_version(alpm_db_t UNUSED *db, const char *dbpath)
380 {
381 	char dbverpath[PATH_MAX];
382 	FILE *dbverfile;
383 
384 	snprintf(dbverpath, PATH_MAX, "%sALPM_DB_VERSION", dbpath);
385 
386 	dbverfile = fopen(dbverpath, "w");
387 
388 	if(dbverfile == NULL) {
389 		return 1;
390 	}
391 
392 	fprintf(dbverfile, "%zu\n", ALPM_LOCAL_DB_VERSION);
393 	fclose(dbverfile);
394 
395 	return 0;
396 }
397 
local_db_create(alpm_db_t * db,const char * dbpath)398 static int local_db_create(alpm_db_t *db, const char *dbpath)
399 {
400 	if(mkdir(dbpath, 0755) != 0) {
401 		_alpm_log(db->handle, ALPM_LOG_ERROR, _("could not create directory %s: %s\n"),
402 				dbpath, strerror(errno));
403 		RET_ERR(db->handle, ALPM_ERR_DB_CREATE, -1);
404 	}
405 	if(local_db_add_version(db, dbpath) != 0) {
406 		return 1;
407 	}
408 
409 	return 0;
410 }
411 
local_db_validate(alpm_db_t * db)412 static int local_db_validate(alpm_db_t *db)
413 {
414 	struct dirent *ent = NULL;
415 	const char *dbpath;
416 	DIR *dbdir;
417 	char dbverpath[PATH_MAX];
418 	FILE *dbverfile;
419 	int t;
420 	size_t version;
421 
422 	if(db->status & DB_STATUS_VALID) {
423 		return 0;
424 	}
425 	if(db->status & DB_STATUS_INVALID) {
426 		return -1;
427 	}
428 
429 	dbpath = _alpm_db_path(db);
430 	if(dbpath == NULL) {
431 		RET_ERR(db->handle, ALPM_ERR_DB_OPEN, -1);
432 	}
433 
434 	dbdir = opendir(dbpath);
435 	if(dbdir == NULL) {
436 		if(errno == ENOENT) {
437 			/* local database dir doesn't exist yet - create it */
438 			if(local_db_create(db, dbpath) == 0) {
439 				db->status |= DB_STATUS_VALID;
440 				db->status &= ~DB_STATUS_INVALID;
441 				db->status |= DB_STATUS_EXISTS;
442 				db->status &= ~DB_STATUS_MISSING;
443 				return 0;
444 			} else {
445 				db->status &= ~DB_STATUS_EXISTS;
446 				db->status |= DB_STATUS_MISSING;
447 				/* pm_errno is set by local_db_create */
448 				return -1;
449 			}
450 		} else {
451 			RET_ERR(db->handle, ALPM_ERR_DB_OPEN, -1);
452 		}
453 	}
454 	db->status |= DB_STATUS_EXISTS;
455 	db->status &= ~DB_STATUS_MISSING;
456 
457 	snprintf(dbverpath, PATH_MAX, "%sALPM_DB_VERSION", dbpath);
458 
459 	if((dbverfile = fopen(dbverpath, "r")) == NULL) {
460 		/* create dbverfile if local database is empty - otherwise version error */
461 		while((ent = readdir(dbdir)) != NULL) {
462 			const char *name = ent->d_name;
463 			if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
464 				continue;
465 			} else {
466 				goto version_error;
467 			}
468 		}
469 
470 		if(local_db_add_version(db, dbpath) != 0) {
471 			goto version_error;
472 		}
473 		goto version_latest;
474 	}
475 
476 	t = fscanf(dbverfile, "%zu", &version);
477 	fclose(dbverfile);
478 
479 	if(t != 1) {
480 		goto version_error;
481 	}
482 
483 	if(version != ALPM_LOCAL_DB_VERSION) {
484 		goto version_error;
485 	}
486 
487 version_latest:
488 	closedir(dbdir);
489 	db->status |= DB_STATUS_VALID;
490 	db->status &= ~DB_STATUS_INVALID;
491 	return 0;
492 
493 version_error:
494 	closedir(dbdir);
495 	db->status &= ~DB_STATUS_VALID;
496 	db->status |= DB_STATUS_INVALID;
497 	db->handle->pm_errno = ALPM_ERR_DB_VERSION;
498 	return -1;
499 }
500 
local_db_populate(alpm_db_t * db)501 static int local_db_populate(alpm_db_t *db)
502 {
503 	size_t est_count;
504 	size_t count = 0;
505 	struct stat buf;
506 	struct dirent *ent = NULL;
507 	const char *dbpath;
508 	DIR *dbdir;
509 
510 	if(db->status & DB_STATUS_INVALID) {
511 		RET_ERR(db->handle, ALPM_ERR_DB_INVALID, -1);
512 	}
513 	if(db->status & DB_STATUS_MISSING) {
514 		RET_ERR(db->handle, ALPM_ERR_DB_NOT_FOUND, -1);
515 	}
516 
517 	dbpath = _alpm_db_path(db);
518 	if(dbpath == NULL) {
519 		/* pm_errno set in _alpm_db_path() */
520 		return -1;
521 	}
522 
523 	dbdir = opendir(dbpath);
524 	if(dbdir == NULL) {
525 		RET_ERR(db->handle, ALPM_ERR_DB_OPEN, -1);
526 	}
527 	if(fstat(dirfd(dbdir), &buf) != 0) {
528 		RET_ERR(db->handle, ALPM_ERR_DB_OPEN, -1);
529 	}
530 	db->status |= DB_STATUS_EXISTS;
531 	db->status &= ~DB_STATUS_MISSING;
532 	if(buf.st_nlink >= 2) {
533 		est_count = buf.st_nlink;
534 	} else {
535 		/* Some filesystems don't subscribe to the two-implicit links school of
536 		 * thought, e.g. BTRFS, HFS+. See
537 		 * http://kerneltrap.org/mailarchive/linux-btrfs/2010/1/23/6723483/thread
538 		 */
539 		est_count = 0;
540 		while(readdir(dbdir) != NULL) {
541 			est_count++;
542 		}
543 		rewinddir(dbdir);
544 	}
545 	if(est_count >= 2) {
546 		/* subtract the '.' and '..' pointers to get # of children */
547 		est_count -= 2;
548 	}
549 
550 	db->pkgcache = _alpm_pkghash_create(est_count);
551 	if(db->pkgcache == NULL){
552 		closedir(dbdir);
553 		RET_ERR(db->handle, ALPM_ERR_MEMORY, -1);
554 	}
555 
556 	while((ent = readdir(dbdir)) != NULL) {
557 		const char *name = ent->d_name;
558 
559 		alpm_pkg_t *pkg;
560 
561 		if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
562 			continue;
563 		}
564 		if(!is_dir(dbpath, ent)) {
565 			continue;
566 		}
567 
568 		pkg = _alpm_pkg_new();
569 		if(pkg == NULL) {
570 			closedir(dbdir);
571 			RET_ERR(db->handle, ALPM_ERR_MEMORY, -1);
572 		}
573 		/* split the db entry name */
574 		if(_alpm_splitname(name, &(pkg->name), &(pkg->version),
575 					&(pkg->name_hash)) != 0) {
576 			_alpm_log(db->handle, ALPM_LOG_ERROR, _("invalid name for database entry '%s'\n"),
577 					name);
578 			_alpm_pkg_free(pkg);
579 			continue;
580 		}
581 
582 		/* duplicated database entries are not allowed */
583 		if(_alpm_pkghash_find(db->pkgcache, pkg->name)) {
584 			_alpm_log(db->handle, ALPM_LOG_ERROR, _("duplicated database entry '%s'\n"), pkg->name);
585 			_alpm_pkg_free(pkg);
586 			continue;
587 		}
588 
589 		pkg->origin = ALPM_PKG_FROM_LOCALDB;
590 		pkg->origin_data.db = db;
591 		pkg->ops = &local_pkg_ops;
592 		pkg->handle = db->handle;
593 
594 		/* explicitly read with only 'BASE' data, accessors will handle the rest */
595 		if(local_db_read(pkg, INFRQ_BASE) == -1) {
596 			_alpm_log(db->handle, ALPM_LOG_ERROR, _("corrupted database entry '%s'\n"), name);
597 			_alpm_pkg_free(pkg);
598 			continue;
599 		}
600 
601 		/* add to the collection */
602 		_alpm_log(db->handle, ALPM_LOG_FUNCTION, "adding '%s' to package cache for db '%s'\n",
603 				pkg->name, db->treename);
604 		if(_alpm_pkghash_add(&db->pkgcache, pkg) == NULL) {
605 			_alpm_pkg_free(pkg);
606 			RET_ERR(db->handle, ALPM_ERR_MEMORY, -1);
607 		}
608 		count++;
609 	}
610 
611 	closedir(dbdir);
612 	if(count > 0) {
613 		db->pkgcache->list = alpm_list_msort(db->pkgcache->list, count, _alpm_pkg_cmp);
614 	}
615 	_alpm_log(db->handle, ALPM_LOG_DEBUG, "added %zu packages to package cache for db '%s'\n",
616 			count, db->treename);
617 
618 	return 0;
619 }
620 
621 /* Note: the return value must be freed by the caller */
_alpm_local_db_pkgpath(alpm_db_t * db,alpm_pkg_t * info,const char * filename)622 char *_alpm_local_db_pkgpath(alpm_db_t *db, alpm_pkg_t *info,
623 		const char *filename)
624 {
625 	size_t len;
626 	char *pkgpath;
627 	const char *dbpath;
628 
629 	dbpath = _alpm_db_path(db);
630 	len = strlen(dbpath) + strlen(info->name) + strlen(info->version) + 3;
631 	len += filename ? strlen(filename) : 0;
632 	MALLOC(pkgpath, len, RET_ERR(db->handle, ALPM_ERR_MEMORY, NULL));
633 	sprintf(pkgpath, "%s%s-%s/%s", dbpath, info->name, info->version,
634 			filename ? filename : "");
635 	return pkgpath;
636 }
637 
638 #define READ_NEXT() do { \
639 	if(safe_fgets(line, sizeof(line), fp) == NULL && !feof(fp)) goto error; \
640 	_alpm_strip_newline(line, 0); \
641 } while(0)
642 
643 #define READ_AND_STORE(f) do { \
644 	READ_NEXT(); \
645 	STRDUP(f, line, goto error); \
646 } while(0)
647 
648 #define READ_AND_STORE_ALL(f) do { \
649 	char *linedup; \
650 	if(safe_fgets(line, sizeof(line), fp) == NULL) {\
651 		if(!feof(fp)) goto error; else break; \
652 	} \
653 	if(_alpm_strip_newline(line, 0) == 0) break; \
654 	STRDUP(linedup, line, goto error); \
655 	f = alpm_list_add(f, linedup); \
656 } while(1) /* note the while(1) and not (0) */
657 
658 #define READ_AND_SPLITDEP(f) do { \
659 	if(safe_fgets(line, sizeof(line), fp) == NULL) {\
660 		if(!feof(fp)) goto error; else break; \
661 	} \
662 	if(_alpm_strip_newline(line, 0) == 0) break; \
663 	f = alpm_list_add(f, alpm_dep_from_string(line)); \
664 } while(1) /* note the while(1) and not (0) */
665 
local_db_read(alpm_pkg_t * info,int inforeq)666 static int local_db_read(alpm_pkg_t *info, int inforeq)
667 {
668 	FILE *fp = NULL;
669 	char line[1024];
670 	alpm_db_t *db = info->origin_data.db;
671 
672 	/* bitmask logic here:
673 	 * infolevel: 00001111
674 	 * inforeq:   00010100
675 	 * & result:  00000100
676 	 * == to inforeq? nope, we need to load more info. */
677 	if((info->infolevel & inforeq) == inforeq) {
678 		/* already loaded all of this info, do nothing */
679 		return 0;
680 	}
681 
682 	if(info->infolevel & INFRQ_ERROR) {
683 		/* We've encountered an error loading this package before. Don't attempt
684 		 * repeated reloads, just give up. */
685 		return -1;
686 	}
687 
688 	_alpm_log(db->handle, ALPM_LOG_FUNCTION,
689 			"loading package data for %s : level=0x%x\n",
690 			info->name, inforeq);
691 
692 	/* clear out 'line', to be certain - and to make valgrind happy */
693 	memset(line, 0, sizeof(line));
694 
695 	/* DESC */
696 	if(inforeq & INFRQ_DESC && !(info->infolevel & INFRQ_DESC)) {
697 		char *path = _alpm_local_db_pkgpath(db, info, "desc");
698 		if(!path || (fp = fopen(path, "r")) == NULL) {
699 			_alpm_log(db->handle, ALPM_LOG_ERROR, _("could not open file %s: %s\n"), path, strerror(errno));
700 			free(path);
701 			goto error;
702 		}
703 		free(path);
704 		while(!feof(fp)) {
705 			if(safe_fgets(line, sizeof(line), fp) == NULL && !feof(fp)) {
706 				goto error;
707 			}
708 			if(_alpm_strip_newline(line, 0) == 0) {
709 				/* length of stripped line was zero */
710 				continue;
711 			}
712 			if(strcmp(line, "%NAME%") == 0) {
713 				READ_NEXT();
714 				if(strcmp(line, info->name) != 0) {
715 					_alpm_log(db->handle, ALPM_LOG_ERROR, _("%s database is inconsistent: name "
716 								"mismatch on package %s\n"), db->treename, info->name);
717 				}
718 			} else if(strcmp(line, "%VERSION%") == 0) {
719 				READ_NEXT();
720 				if(strcmp(line, info->version) != 0) {
721 					_alpm_log(db->handle, ALPM_LOG_ERROR, _("%s database is inconsistent: version "
722 								"mismatch on package %s\n"), db->treename, info->name);
723 				}
724 			} else if(strcmp(line, "%BASE%") == 0) {
725 				READ_AND_STORE(info->base);
726 			} else if(strcmp(line, "%DESC%") == 0) {
727 				READ_AND_STORE(info->desc);
728 			} else if(strcmp(line, "%GROUPS%") == 0) {
729 				READ_AND_STORE_ALL(info->groups);
730 			} else if(strcmp(line, "%URL%") == 0) {
731 				READ_AND_STORE(info->url);
732 			} else if(strcmp(line, "%LICENSE%") == 0) {
733 				READ_AND_STORE_ALL(info->licenses);
734 			} else if(strcmp(line, "%ARCH%") == 0) {
735 				READ_AND_STORE(info->arch);
736 			} else if(strcmp(line, "%BUILDDATE%") == 0) {
737 				READ_NEXT();
738 				info->builddate = _alpm_parsedate(line);
739 			} else if(strcmp(line, "%INSTALLDATE%") == 0) {
740 				READ_NEXT();
741 				info->installdate = _alpm_parsedate(line);
742 			} else if(strcmp(line, "%PACKAGER%") == 0) {
743 				READ_AND_STORE(info->packager);
744 			} else if(strcmp(line, "%REASON%") == 0) {
745 				READ_NEXT();
746 				info->reason = (alpm_pkgreason_t)atoi(line);
747 			} else if(strcmp(line, "%VALIDATION%") == 0) {
748 				alpm_list_t *i, *v = NULL;
749 				READ_AND_STORE_ALL(v);
750 				for(i = v; i; i = alpm_list_next(i))
751 				{
752 					if(strcmp(i->data, "none") == 0) {
753 						info->validation |= ALPM_PKG_VALIDATION_NONE;
754 					} else if(strcmp(i->data, "md5") == 0) {
755 						info->validation |= ALPM_PKG_VALIDATION_MD5SUM;
756 					} else if(strcmp(i->data, "sha256") == 0) {
757 						info->validation |= ALPM_PKG_VALIDATION_SHA256SUM;
758 					} else if(strcmp(i->data, "pgp") == 0) {
759 						info->validation |= ALPM_PKG_VALIDATION_SIGNATURE;
760 					} else {
761 						_alpm_log(db->handle, ALPM_LOG_WARNING,
762 								_("unknown validation type for package %s: %s\n"),
763 								info->name, (const char *)i->data);
764 					}
765 				}
766 				FREELIST(v);
767 			} else if(strcmp(line, "%SIZE%") == 0) {
768 				READ_NEXT();
769 				info->isize = _alpm_strtoofft(line);
770 			} else if(strcmp(line, "%REPLACES%") == 0) {
771 				READ_AND_SPLITDEP(info->replaces);
772 			} else if(strcmp(line, "%DEPENDS%") == 0) {
773 				READ_AND_SPLITDEP(info->depends);
774 			} else if(strcmp(line, "%OPTDEPENDS%") == 0) {
775 				READ_AND_SPLITDEP(info->optdepends);
776 			} else if(strcmp(line, "%CONFLICTS%") == 0) {
777 				READ_AND_SPLITDEP(info->conflicts);
778 			} else if(strcmp(line, "%PROVIDES%") == 0) {
779 				READ_AND_SPLITDEP(info->provides);
780 			}
781 		}
782 		fclose(fp);
783 		fp = NULL;
784 		info->infolevel |= INFRQ_DESC;
785 	}
786 
787 	/* FILES */
788 	if(inforeq & INFRQ_FILES && !(info->infolevel & INFRQ_FILES)) {
789 		char *path = _alpm_local_db_pkgpath(db, info, "files");
790 		if(!path || (fp = fopen(path, "r")) == NULL) {
791 			_alpm_log(db->handle, ALPM_LOG_ERROR, _("could not open file %s: %s\n"), path, strerror(errno));
792 			free(path);
793 			goto error;
794 		}
795 		free(path);
796 		while(safe_fgets(line, sizeof(line), fp)) {
797 			_alpm_strip_newline(line, 0);
798 			if(strcmp(line, "%FILES%") == 0) {
799 				size_t files_count = 0, files_size = 0, len;
800 				alpm_file_t *files = NULL;
801 
802 				while(safe_fgets(line, sizeof(line), fp) &&
803 						(len = _alpm_strip_newline(line, 0))) {
804 					if(!_alpm_greedy_grow((void **)&files, &files_size,
805 								(files_count ? (files_count + 1) * sizeof(alpm_file_t) : 8 * sizeof(alpm_file_t)))) {
806 						goto nomem;
807 					}
808 					/* since we know the length of the file string already,
809 					 * we can do malloc + memcpy rather than strdup */
810 					len += 1;
811 					MALLOC(files[files_count].name, len, goto nomem);
812 					memcpy(files[files_count].name, line, len);
813 					files_count++;
814 				}
815 				/* attempt to hand back any memory we don't need */
816 				if(files_count > 0) {
817 					alpm_file_t *newfiles;
818 
819 					newfiles = realloc(files, sizeof(alpm_file_t) * files_count);
820 					if(newfiles != NULL) {
821 						files = newfiles;
822 					}
823 				} else {
824 					FREE(files);
825 				}
826 				info->files.count = files_count;
827 				info->files.files = files;
828 				_alpm_filelist_sort(&info->files);
829 				continue;
830 nomem:
831 				while(files_count > 0) {
832 					FREE(files[--files_count].name);
833 				}
834 				FREE(files);
835 				goto error;
836 			} else if(strcmp(line, "%BACKUP%") == 0) {
837 				while(safe_fgets(line, sizeof(line), fp) && _alpm_strip_newline(line, 0)) {
838 					alpm_backup_t *backup;
839 					CALLOC(backup, 1, sizeof(alpm_backup_t), goto error);
840 					if(_alpm_split_backup(line, &backup)) {
841 						FREE(backup);
842 						goto error;
843 					}
844 					info->backup = alpm_list_add(info->backup, backup);
845 				}
846 			}
847 		}
848 		fclose(fp);
849 		fp = NULL;
850 		info->infolevel |= INFRQ_FILES;
851 	}
852 
853 	/* INSTALL */
854 	if(inforeq & INFRQ_SCRIPTLET && !(info->infolevel & INFRQ_SCRIPTLET)) {
855 		char *path = _alpm_local_db_pkgpath(db, info, "install");
856 		if(access(path, F_OK) == 0) {
857 			info->scriptlet = 1;
858 		}
859 		free(path);
860 		info->infolevel |= INFRQ_SCRIPTLET;
861 	}
862 
863 	return 0;
864 
865 error:
866 	info->infolevel |= INFRQ_ERROR;
867 	if(fp) {
868 		fclose(fp);
869 	}
870 	return -1;
871 }
872 
_alpm_local_db_prepare(alpm_db_t * db,alpm_pkg_t * info)873 int _alpm_local_db_prepare(alpm_db_t *db, alpm_pkg_t *info)
874 {
875 	mode_t oldmask;
876 	int retval = 0;
877 	char *pkgpath;
878 
879 	if(checkdbdir(db) != 0) {
880 		return -1;
881 	}
882 
883 	oldmask = umask(0000);
884 	pkgpath = _alpm_local_db_pkgpath(db, info, NULL);
885 
886 	if((retval = mkdir(pkgpath, 0755)) != 0) {
887 		_alpm_log(db->handle, ALPM_LOG_ERROR, _("could not create directory %s: %s\n"),
888 				pkgpath, strerror(errno));
889 	}
890 
891 	free(pkgpath);
892 	umask(oldmask);
893 
894 	return retval;
895 }
896 
write_deps(FILE * fp,const char * header,alpm_list_t * deplist)897 static void write_deps(FILE *fp, const char *header, alpm_list_t *deplist)
898 {
899 	alpm_list_t *lp;
900 	if(!deplist) {
901 		return;
902 	}
903 	fputs(header, fp);
904 	fputc('\n', fp);
905 	for(lp = deplist; lp; lp = lp->next) {
906 		char *depstring = alpm_dep_compute_string(lp->data);
907 		fputs(depstring, fp);
908 		fputc('\n', fp);
909 		free(depstring);
910 	}
911 	fputc('\n', fp);
912 }
913 
_alpm_local_db_write(alpm_db_t * db,alpm_pkg_t * info,int inforeq)914 int _alpm_local_db_write(alpm_db_t *db, alpm_pkg_t *info, int inforeq)
915 {
916 	FILE *fp = NULL;
917 	mode_t oldmask;
918 	alpm_list_t *lp;
919 	int retval = 0;
920 
921 	if(db == NULL || info == NULL || !(db->status & DB_STATUS_LOCAL)) {
922 		return -1;
923 	}
924 
925 	/* make sure we have a sane umask */
926 	oldmask = umask(0022);
927 
928 	/* DESC */
929 	if(inforeq & INFRQ_DESC) {
930 		char *path;
931 		_alpm_log(db->handle, ALPM_LOG_DEBUG,
932 				"writing %s-%s DESC information back to db\n",
933 				info->name, info->version);
934 		path = _alpm_local_db_pkgpath(db, info, "desc");
935 		if(!path || (fp = fopen(path, "w")) == NULL) {
936 			_alpm_log(db->handle, ALPM_LOG_ERROR, _("could not open file %s: %s\n"),
937 					path, strerror(errno));
938 			retval = -1;
939 			free(path);
940 			goto cleanup;
941 		}
942 		free(path);
943 		fprintf(fp, "%%NAME%%\n%s\n\n"
944 						"%%VERSION%%\n%s\n\n", info->name, info->version);
945 		if(info->base) {
946 			fprintf(fp, "%%BASE%%\n"
947 							"%s\n\n", info->base);
948 		}
949 		if(info->desc) {
950 			fprintf(fp, "%%DESC%%\n"
951 							"%s\n\n", info->desc);
952 		}
953 		if(info->url) {
954 			fprintf(fp, "%%URL%%\n"
955 							"%s\n\n", info->url);
956 		}
957 		if(info->arch) {
958 			fprintf(fp, "%%ARCH%%\n"
959 							"%s\n\n", info->arch);
960 		}
961 		if(info->builddate) {
962 			fprintf(fp, "%%BUILDDATE%%\n"
963 							"%jd\n\n", (intmax_t)info->builddate);
964 		}
965 		if(info->installdate) {
966 			fprintf(fp, "%%INSTALLDATE%%\n"
967 							"%jd\n\n", (intmax_t)info->installdate);
968 		}
969 		if(info->packager) {
970 			fprintf(fp, "%%PACKAGER%%\n"
971 							"%s\n\n", info->packager);
972 		}
973 		if(info->isize) {
974 			/* only write installed size, csize is irrelevant once installed */
975 			fprintf(fp, "%%SIZE%%\n"
976 							"%jd\n\n", (intmax_t)info->isize);
977 		}
978 		if(info->reason) {
979 			fprintf(fp, "%%REASON%%\n"
980 							"%u\n\n", info->reason);
981 		}
982 		if(info->groups) {
983 			fputs("%GROUPS%\n", fp);
984 			for(lp = info->groups; lp; lp = lp->next) {
985 				fputs(lp->data, fp);
986 				fputc('\n', fp);
987 			}
988 			fputc('\n', fp);
989 		}
990 		if(info->licenses) {
991 			fputs("%LICENSE%\n", fp);
992 			for(lp = info->licenses; lp; lp = lp->next) {
993 				fputs(lp->data, fp);
994 				fputc('\n', fp);
995 			}
996 			fputc('\n', fp);
997 		}
998 		if(info->validation) {
999 			fputs("%VALIDATION%\n", fp);
1000 			if(info->validation & ALPM_PKG_VALIDATION_NONE) {
1001 				fputs("none\n", fp);
1002 			}
1003 			if(info->validation & ALPM_PKG_VALIDATION_MD5SUM) {
1004 				fputs("md5\n", fp);
1005 			}
1006 			if(info->validation & ALPM_PKG_VALIDATION_SHA256SUM) {
1007 				fputs("sha256\n", fp);
1008 			}
1009 			if(info->validation & ALPM_PKG_VALIDATION_SIGNATURE) {
1010 				fputs("pgp\n", fp);
1011 			}
1012 			fputc('\n', fp);
1013 		}
1014 
1015 		write_deps(fp, "%REPLACES%", info->replaces);
1016 		write_deps(fp, "%DEPENDS%", info->depends);
1017 		write_deps(fp, "%OPTDEPENDS%", info->optdepends);
1018 		write_deps(fp, "%CONFLICTS%", info->conflicts);
1019 		write_deps(fp, "%PROVIDES%", info->provides);
1020 
1021 		fclose(fp);
1022 		fp = NULL;
1023 	}
1024 
1025 	/* FILES */
1026 	if(inforeq & INFRQ_FILES) {
1027 		char *path;
1028 		_alpm_log(db->handle, ALPM_LOG_DEBUG,
1029 				"writing %s-%s FILES information back to db\n",
1030 				info->name, info->version);
1031 		path = _alpm_local_db_pkgpath(db, info, "files");
1032 		if(!path || (fp = fopen(path, "w")) == NULL) {
1033 			_alpm_log(db->handle, ALPM_LOG_ERROR, _("could not open file %s: %s\n"),
1034 					path, strerror(errno));
1035 			retval = -1;
1036 			free(path);
1037 			goto cleanup;
1038 		}
1039 		free(path);
1040 		if(info->files.count) {
1041 			size_t i;
1042 			fputs("%FILES%\n", fp);
1043 			for(i = 0; i < info->files.count; i++) {
1044 				const alpm_file_t *file = info->files.files + i;
1045 				fputs(file->name, fp);
1046 				fputc('\n', fp);
1047 			}
1048 			fputc('\n', fp);
1049 		}
1050 		if(info->backup) {
1051 			fputs("%BACKUP%\n", fp);
1052 			for(lp = info->backup; lp; lp = lp->next) {
1053 				const alpm_backup_t *backup = lp->data;
1054 				fprintf(fp, "%s\t%s\n", backup->name, backup->hash);
1055 			}
1056 			fputc('\n', fp);
1057 		}
1058 		fclose(fp);
1059 		fp = NULL;
1060 	}
1061 
1062 	/* INSTALL and MTREE */
1063 	/* nothing needed here (automatically extracted) */
1064 
1065 cleanup:
1066 	umask(oldmask);
1067 	return retval;
1068 }
1069 
_alpm_local_db_remove(alpm_db_t * db,alpm_pkg_t * info)1070 int _alpm_local_db_remove(alpm_db_t *db, alpm_pkg_t *info)
1071 {
1072 	int ret = 0;
1073 	DIR *dirp;
1074 	struct dirent *dp;
1075 	char *pkgpath;
1076 	size_t pkgpath_len;
1077 
1078 	pkgpath = _alpm_local_db_pkgpath(db, info, NULL);
1079 	if(!pkgpath) {
1080 		return -1;
1081 	}
1082 	pkgpath_len = strlen(pkgpath);
1083 
1084 	dirp = opendir(pkgpath);
1085 	if(!dirp) {
1086 		free(pkgpath);
1087 		return -1;
1088 	}
1089 	/* go through the local DB entry, removing the files within, which we know
1090 	 * are not nested directories of any kind. */
1091 	for(dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
1092 		if(strcmp(dp->d_name, "..") != 0 && strcmp(dp->d_name, ".") != 0) {
1093 			char name[PATH_MAX];
1094 			if(pkgpath_len + strlen(dp->d_name) + 2 > PATH_MAX) {
1095 				/* file path is too long to remove, hmm. */
1096 				ret = -1;
1097 			} else {
1098 				sprintf(name, "%s/%s", pkgpath, dp->d_name);
1099 				if(unlink(name)) {
1100 					ret = -1;
1101 				}
1102 			}
1103 		}
1104 	}
1105 	closedir(dirp);
1106 
1107 	/* after removing all enclosed files, we can remove the directory itself. */
1108 	if(rmdir(pkgpath)) {
1109 		ret = -1;
1110 	}
1111 	free(pkgpath);
1112 	return ret;
1113 }
1114 
alpm_pkg_set_reason(alpm_pkg_t * pkg,alpm_pkgreason_t reason)1115 int SYMEXPORT alpm_pkg_set_reason(alpm_pkg_t *pkg, alpm_pkgreason_t reason)
1116 {
1117 	ASSERT(pkg != NULL, return -1);
1118 	ASSERT(pkg->origin == ALPM_PKG_FROM_LOCALDB,
1119 			RET_ERR(pkg->handle, ALPM_ERR_WRONG_ARGS, -1));
1120 	ASSERT(pkg->origin_data.db == pkg->handle->db_local,
1121 			RET_ERR(pkg->handle, ALPM_ERR_WRONG_ARGS, -1));
1122 
1123 	_alpm_log(pkg->handle, ALPM_LOG_DEBUG,
1124 			"setting install reason %u for %s\n", reason, pkg->name);
1125 	if(alpm_pkg_get_reason(pkg) == reason) {
1126 		/* we are done */
1127 		return 0;
1128 	}
1129 	/* set reason (in pkgcache) */
1130 	pkg->reason = reason;
1131 	/* write DESC */
1132 	if(_alpm_local_db_write(pkg->handle->db_local, pkg, INFRQ_DESC)) {
1133 		RET_ERR(pkg->handle, ALPM_ERR_DB_WRITE, -1);
1134 	}
1135 
1136 	return 0;
1137 }
1138 
1139 struct db_operations local_db_ops = {
1140 	.validate         = local_db_validate,
1141 	.populate         = local_db_populate,
1142 	.unregister       = _alpm_db_unregister,
1143 };
1144 
_alpm_db_register_local(alpm_handle_t * handle)1145 alpm_db_t *_alpm_db_register_local(alpm_handle_t *handle)
1146 {
1147 	alpm_db_t *db;
1148 
1149 	_alpm_log(handle, ALPM_LOG_DEBUG, "registering local database\n");
1150 
1151 	db = _alpm_db_new("local", 1);
1152 	if(db == NULL) {
1153 		handle->pm_errno = ALPM_ERR_DB_CREATE;
1154 		return NULL;
1155 	}
1156 	db->ops = &local_db_ops;
1157 	db->handle = handle;
1158 	db->usage = ALPM_DB_USAGE_ALL;
1159 
1160 	if(local_db_validate(db)) {
1161 		/* pm_errno set in local_db_validate() */
1162 		_alpm_db_free(db);
1163 		return NULL;
1164 	}
1165 
1166 	handle->db_local = db;
1167 	return db;
1168 }
1169