Exemplo n.º 1
0
static int local_db_validate(pmdb_t *db)
{
    struct dirent *ent = NULL;
    const char *dbpath;
    DIR *dbdir;
    int ret = -1;

    if(db->status & DB_STATUS_VALID) {
        return 0;
    }

    dbpath = _alpm_db_path(db);
    if(dbpath == NULL) {
        RET_ERR(db->handle, PM_ERR_DB_OPEN, -1);
    }
    dbdir = opendir(dbpath);
    if(dbdir == NULL) {
        if(errno == ENOENT) {
            /* database dir doesn't exist yet */
            db->status |= DB_STATUS_VALID;
            return 0;
        } else {
            RET_ERR(db->handle, PM_ERR_DB_OPEN, -1);
        }
    }

    while((ent = readdir(dbdir)) != NULL) {
        const char *name = ent->d_name;
        char path[PATH_MAX];

        if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
            continue;
        }
        if(!is_dir(dbpath, ent)) {
            continue;
        }

        snprintf(path, PATH_MAX, "%s%s/depends", dbpath, name);
        if(access(path, F_OK) == 0) {
            /* we found a depends file- bail */
            db->handle->pm_errno = PM_ERR_DB_VERSION;
            goto done;
        }
    }
    /* we found no depends file after full scan */
    db->status |= DB_STATUS_VALID;
    ret = 0;

done:
    if(dbdir) {
        closedir(dbdir);
    }

    return ret;
}
Exemplo n.º 2
0
/* Note: the return value must be freed by the caller */
static char *get_pkgpath(pmdb_t *db, pmpkg_t *info)
{
    size_t len;
    char *pkgpath;
    const char *dbpath;

    dbpath = _alpm_db_path(db);
    len = strlen(dbpath) + strlen(info->name) + strlen(info->version) + 3;
    MALLOC(pkgpath, len, RET_ERR(db->handle, PM_ERR_MEMORY, NULL));
    sprintf(pkgpath, "%s%s-%s/", dbpath, info->name, info->version);
    return pkgpath;
}
Exemplo n.º 3
0
/* Note: the return value must be freed by the caller */
char *_alpm_local_db_pkgpath(alpm_db_t *db, alpm_pkg_t *info, const char *filename)
{
	size_t len;
	char *pkgpath;
	const char *dbpath;

	dbpath = _alpm_db_path(db);
	len = strlen(dbpath) + strlen(info->name) + strlen(info->version) + 3;
	len += filename ? strlen(filename) : 0;
	MALLOC(pkgpath, len, RET_ERR(db->handle, ALPM_ERR_MEMORY, NULL));
	sprintf(pkgpath, "%s%s-%s/%s", dbpath, info->name, info->version,
			filename ? filename : "");
	return pkgpath;
}
Exemplo n.º 4
0
static int checkdbdir(alpm_db_t *db)
{
	struct stat buf;
	const char *path = _alpm_db_path(db);

	if(stat(path, &buf) != 0) {
		_alpm_log(db->handle, ALPM_LOG_DEBUG, "database dir '%s' does not exist, creating it\n",
				path);
		if(_alpm_makepath(path) != 0) {
			RET_ERR(db->handle, ALPM_ERR_SYSTEM, -1);
		}
	} else if(!S_ISDIR(buf.st_mode)) {
		_alpm_log(db->handle, ALPM_LOG_WARNING, _("removing invalid database: %s\n"), path);
		if(unlink(path) != 0 || _alpm_makepath(path) != 0) {
			RET_ERR(db->handle, ALPM_ERR_SYSTEM, -1);
		}
	}
	return 0;
}
Exemplo n.º 5
0
static int extract_db_file(alpm_handle_t *handle, struct archive *archive,
		struct archive_entry *entry, alpm_pkg_t *newpkg, const char *entryname)
{
	char filename[PATH_MAX]; /* the actual file we're extracting */
	const char *dbfile = NULL;
	if(strcmp(entryname, ".INSTALL") == 0) {
		dbfile = "install";
	} else if(strcmp(entryname, ".CHANGELOG") == 0) {
		dbfile = "changelog";
	} else if(strcmp(entryname, ".MTREE") == 0) {
		dbfile = "mtree";
	} else if(*entryname == '.') {
		/* reserve all files starting with '.' for future possibilities */
		_alpm_log(handle, ALPM_LOG_DEBUG, "skipping extraction of '%s'\n", entryname);
		archive_read_data_skip(archive);
		return 0;
	}
	archive_entry_set_perm(entry, 0644);
	snprintf(filename, PATH_MAX, "%s%s-%s/%s",
			_alpm_db_path(handle->db_local), newpkg->name, newpkg->version, dbfile);
	return perform_extraction(handle, archive, entry, filename);
}
Exemplo n.º 6
0
static int local_db_populate(alpm_db_t *db)
{
	size_t est_count;
	int count = 0;
	struct stat buf;
	struct dirent *ent = NULL;
	const char *dbpath;
	DIR *dbdir;

	if(db->status & DB_STATUS_INVALID) {
		RET_ERR(db->handle, ALPM_ERR_DB_INVALID, -1);
	}
	/* note: DB_STATUS_MISSING is not fatal for local database */

	dbpath = _alpm_db_path(db);
	if(dbpath == NULL) {
		/* pm_errno set in _alpm_db_path() */
		return -1;
	}

	dbdir = opendir(dbpath);
	if(dbdir == NULL) {
		if(errno == ENOENT) {
			/* no database existing yet is not an error */
			db->status &= ~DB_STATUS_EXISTS;
			db->status |= DB_STATUS_MISSING;
			return 0;
		}
		RET_ERR(db->handle, ALPM_ERR_DB_OPEN, -1);
	}
	if(fstat(dirfd(dbdir), &buf) != 0) {
		RET_ERR(db->handle, ALPM_ERR_DB_OPEN, -1);
	}
	db->status |= DB_STATUS_EXISTS;
	db->status &= ~DB_STATUS_MISSING;
	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(readdir(dbdir) != NULL) {
			est_count++;
		}
		rewinddir(dbdir);
	}
	if(est_count >= 2) {
		/* subtract the two extra pointers to get # of children */
		est_count -= 2;
	}

	/* initialize hash at 50% full */
	db->pkgcache = _alpm_pkghash_create(est_count * 2);
	if(db->pkgcache == NULL){
		closedir(dbdir);
		RET_ERR(db->handle, ALPM_ERR_MEMORY, -1);
	}

	while((ent = readdir(dbdir)) != NULL) {
		const char *name = ent->d_name;

		alpm_pkg_t *pkg;

		if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
			continue;
		}
		if(!is_dir(dbpath, ent)) {
			continue;
		}

		pkg = _alpm_pkg_new();
		if(pkg == NULL) {
			closedir(dbdir);
			RET_ERR(db->handle, ALPM_ERR_MEMORY, -1);
		}
		/* split the db entry name */
		if(_alpm_splitname(name, &(pkg->name), &(pkg->version),
					&(pkg->name_hash)) != 0) {
			_alpm_log(db->handle, ALPM_LOG_ERROR, _("invalid name for database entry '%s'\n"),
					name);
			_alpm_pkg_free(pkg);
			continue;
		}

		/* duplicated database entries are not allowed */
		if(_alpm_pkghash_find(db->pkgcache, pkg->name)) {
			_alpm_log(db->handle, ALPM_LOG_ERROR, _("duplicated database entry '%s'\n"), pkg->name);
			_alpm_pkg_free(pkg);
			continue;
		}

		pkg->origin = PKG_FROM_LOCALDB;
		pkg->origin_data.db = db;
		pkg->ops = &local_pkg_ops;
		pkg->handle = db->handle;

		/* explicitly read with only 'BASE' data, accessors will handle the rest */
		if(local_db_read(pkg, INFRQ_BASE) == -1) {
			_alpm_log(db->handle, ALPM_LOG_ERROR, _("corrupted database entry '%s'\n"), name);
			_alpm_pkg_free(pkg);
			continue;
		}

		/* add to the collection */
		_alpm_log(db->handle, ALPM_LOG_FUNCTION, "adding '%s' to package cache for db '%s'\n",
				pkg->name, db->treename);
		db->pkgcache = _alpm_pkghash_add(db->pkgcache, pkg);
		count++;
	}

	closedir(dbdir);
	if(count > 0) {
		db->pkgcache->list = alpm_list_msort(db->pkgcache->list, (size_t)count, _alpm_pkg_cmp);
	}
	_alpm_log(db->handle, ALPM_LOG_DEBUG, "added %d packages to package cache for db '%s'\n",
			count, db->treename);

	return count;
}
Exemplo n.º 7
0
static int local_db_validate(alpm_db_t *db)
{
	struct dirent *ent = NULL;
	const char *dbpath;
	DIR *dbdir;
	char dbverpath[PATH_MAX];
	FILE *dbverfile;
	int t;
	size_t version;

	if(db->status & DB_STATUS_VALID) {
		return 0;
	}
	if(db->status & DB_STATUS_INVALID) {
		return -1;
	}

	dbpath = _alpm_db_path(db);
	if(dbpath == NULL) {
		RET_ERR(db->handle, ALPM_ERR_DB_OPEN, -1);
	}

	dbdir = opendir(dbpath);
	if(dbdir == NULL) {
		if(errno == ENOENT) {
			/* local database dir doesn't exist yet - create it */
			if(local_db_create(db, dbpath) == 0) {
				db->status |= DB_STATUS_VALID;
				db->status &= ~DB_STATUS_INVALID;
				db->status |= DB_STATUS_EXISTS;
				db->status &= ~DB_STATUS_MISSING;
				return 0;
			} else {
				db->status &= ~DB_STATUS_EXISTS;
				db->status |= DB_STATUS_MISSING;
				/* pm_errno is set by local_db_create */
				return -1;
			}
		} else {
			RET_ERR(db->handle, ALPM_ERR_DB_OPEN, -1);
		}
	}
	db->status |= DB_STATUS_EXISTS;
	db->status &= ~DB_STATUS_MISSING;

	snprintf(dbverpath, PATH_MAX, "%sALPM_DB_VERSION", dbpath);

	if((dbverfile = fopen(dbverpath, "r")) == NULL) {
		/* create dbverfile if local database is empty - otherwise version error */
		while((ent = readdir(dbdir)) != NULL) {
			const char *name = ent->d_name;
			if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
				continue;
			} else {
				goto version_error;
			}
		}

		if(local_db_add_version(db, dbpath) != 0) {
			goto version_error;
		}
		goto version_latest;
	}

	t = fscanf(dbverfile, "%zu", &version);
	fclose(dbverfile);

	if(t != 1) {
		goto version_error;
	}

	if(version != ALPM_LOCAL_DB_VERSION) {
		goto version_error;
	}

version_latest:
	closedir(dbdir);
	db->status |= DB_STATUS_VALID;
	db->status &= ~DB_STATUS_INVALID;
	return 0;

version_error:
	closedir(dbdir);
	db->status &= ~DB_STATUS_VALID;
	db->status |= DB_STATUS_INVALID;
	db->handle->pm_errno = ALPM_ERR_DB_VERSION;
	return -1;
}
Exemplo n.º 8
0
static int sync_db_validate(alpm_db_t *db)
{
	alpm_siglevel_t level;
	const char *dbpath;

	if(db->status & DB_STATUS_VALID || db->status & DB_STATUS_MISSING) {
		return 0;
	}
	if(db->status & DB_STATUS_INVALID) {
		db->handle->pm_errno = ALPM_ERR_DB_INVALID_SIG;
		return -1;
	}

	dbpath = _alpm_db_path(db);
	if(!dbpath) {
		/* pm_errno set in _alpm_db_path() */
		return -1;
	}

	/* we can skip any validation if the database doesn't exist */
	if(_alpm_access(db->handle, NULL, dbpath, R_OK) != 0 && errno == ENOENT) {
		db->status &= ~DB_STATUS_EXISTS;
		db->status |= DB_STATUS_MISSING;
		EVENT(db->handle, ALPM_EVENT_DATABASE_MISSING, db->treename, NULL);
		goto valid;
	}
	db->status |= DB_STATUS_EXISTS;
	db->status &= ~DB_STATUS_MISSING;

	/* this takes into account the default verification level if UNKNOWN
	 * was assigned to this db */
	level = alpm_db_get_siglevel(db);

	if(level & ALPM_SIG_DATABASE) {
		int retry, ret;
		do {
			retry = 0;
			alpm_siglist_t *siglist;
			ret = _alpm_check_pgp_helper(db->handle, dbpath, NULL,
					level & ALPM_SIG_DATABASE_OPTIONAL, level & ALPM_SIG_DATABASE_MARGINAL_OK,
					level & ALPM_SIG_DATABASE_UNKNOWN_OK, &siglist);
			if(ret) {
				retry = _alpm_process_siglist(db->handle, db->treename, siglist,
						level & ALPM_SIG_DATABASE_OPTIONAL, level & ALPM_SIG_DATABASE_MARGINAL_OK,
						level & ALPM_SIG_DATABASE_UNKNOWN_OK);
			}
			alpm_siglist_cleanup(siglist);
			free(siglist);
		} while(retry);

		if(ret) {
			db->status &= ~DB_STATUS_VALID;
			db->status |= DB_STATUS_INVALID;
			db->handle->pm_errno = ALPM_ERR_DB_INVALID_SIG;
			return 1;
		}
	}

valid:
	db->status |= DB_STATUS_VALID;
	db->status &= ~DB_STATUS_INVALID;
	return 0;
}
Exemplo n.º 9
0
static int sync_db_populate(alpm_db_t *db)
{
	const char *dbpath;
	size_t est_count;
	int count, fd;
	struct stat buf;
	struct archive *archive;
	struct archive_entry *entry;
	alpm_pkg_t *pkg = NULL;

	if(db->status & DB_STATUS_INVALID) {
		RET_ERR(db->handle, ALPM_ERR_DB_INVALID, -1);
	}
	if(db->status & DB_STATUS_MISSING) {
		RET_ERR(db->handle, ALPM_ERR_DB_NOT_FOUND, -1);
	}
	dbpath = _alpm_db_path(db);
	if(!dbpath) {
		/* pm_errno set in _alpm_db_path() */
		return -1;
	}

	fd = _alpm_open_archive(db->handle, dbpath, &buf,
			&archive, ALPM_ERR_DB_OPEN);
	if(fd < 0) {
		return -1;
	}
	est_count = estimate_package_count(&buf, archive);

	db->pkgcache = _alpm_pkghash_create(est_count);
	if(db->pkgcache == NULL) {
		db->handle->pm_errno = ALPM_ERR_MEMORY;
		count = -1;
		goto cleanup;
	}

	while(archive_read_next_header(archive, &entry) == ARCHIVE_OK) {
		mode_t mode = archive_entry_mode(entry);
		if(S_ISDIR(mode)) {
			continue;
		} else {
			/* we have desc, depends or deltas - parse it */
			if(sync_db_read(db, archive, entry, &pkg) != 0) {
				_alpm_log(db->handle, ALPM_LOG_ERROR,
						_("could not parse package description file '%s' from db '%s'\n"),
						archive_entry_pathname(entry), db->treename);
				continue;
			}
		}
	}

	count = alpm_list_count(db->pkgcache->list);
	if(count > 0) {
		db->pkgcache->list = alpm_list_msort(db->pkgcache->list,
				(size_t)count, _alpm_pkg_cmp);
	}
	_alpm_log(db->handle, ALPM_LOG_DEBUG,
			"added %d packages to package cache for db '%s'\n",
			count, db->treename);

cleanup:
	archive_read_finish(archive);
	if(fd >= 0) {
		CLOSE(fd);
	}
	return count;
}
Exemplo n.º 10
0
/** Update a package database
 *
 * An update of the package database \a db will be attempted. Unless
 * \a force is true, the update will only be performed if the remote
 * database was modified since the last update.
 *
 * This operation requires a database lock, and will return an applicable error
 * if the lock could not be obtained.
 *
 * Example:
 * @code
 * alpm_list_t *syncs = alpm_get_syncdbs();
 * for(i = syncs; i; i = alpm_list_next(i)) {
 *     alpm_db_t *db = alpm_list_getdata(i);
 *     result = alpm_db_update(0, db);
 *
 *     if(result < 0) {
 *	       printf("Unable to update database: %s\n", alpm_strerrorlast());
 *     } else if(result == 1) {
 *         printf("Database already up to date\n");
 *     } else {
 *         printf("Database updated\n");
 *     }
 * }
 * @endcode
 *
 * @ingroup alpm_databases
 * @note After a successful update, the \link alpm_db_get_pkgcache()
 * package cache \endlink will be invalidated
 * @param force if true, then forces the update, otherwise update only in case
 * the database isn't up to date
 * @param db pointer to the package database to update
 * @return 0 on success, -1 on error (pm_errno is set accordingly), 1 if up to
 * to date
 */
int SYMEXPORT alpm_db_update(int force, alpm_db_t *db)
{
	char *syncpath;
	alpm_list_t *i;
	int ret = -1;
	mode_t oldmask;
	alpm_handle_t *handle;
	alpm_siglevel_t level;

	/* Sanity checks */
	ASSERT(db != NULL, return -1);
	handle = db->handle;
	handle->pm_errno = 0;
	ASSERT(db != handle->db_local, RET_ERR(handle, ALPM_ERR_WRONG_ARGS, -1));
	ASSERT(db->servers != NULL, RET_ERR(handle, ALPM_ERR_SERVER_NONE, -1));

	syncpath = get_sync_dir(handle);
	if(!syncpath) {
		return -1;
	}

	/* make sure we have a sane umask */
	oldmask = umask(0022);

	level = alpm_db_get_siglevel(db);

	/* attempt to grab a lock */
	if(_alpm_handle_lock(handle)) {
		free(syncpath);
		umask(oldmask);
		RET_ERR(handle, ALPM_ERR_HANDLE_LOCK, -1);
	}

	for(i = db->servers; i; i = i->next) {
		const char *server = i->data;
		struct dload_payload payload;
		size_t len;
		int sig_ret = 0;

		memset(&payload, 0, sizeof(struct dload_payload));

		/* set hard upper limit of 25MiB */
		payload.max_size = 25 * 1024 * 1024;

		/* print server + filename into a buffer */
		len = strlen(server) + strlen(db->treename) + 5;
		/* TODO fix leak syncpath and umask unset */
		MALLOC(payload.fileurl, len, RET_ERR(handle, ALPM_ERR_MEMORY, -1));
		snprintf(payload.fileurl, len, "%s/%s.db", server, db->treename);
		payload.handle = handle;
		payload.force = force;
		payload.unlink_on_fail = 1;

		ret = _alpm_download(&payload, syncpath, NULL);
		_alpm_dload_payload_reset(&payload);

		if(ret == 0 && (level & ALPM_SIG_DATABASE)) {
			/* an existing sig file is no good at this point */
			char *sigpath = _alpm_sigpath(handle, _alpm_db_path(db));
			if(!sigpath) {
				ret = -1;
				break;
			}
			unlink(sigpath);
			free(sigpath);

			/* if we downloaded a DB, we want the .sig from the same server */
			/* print server + filename into a buffer (leave space for .sig) */
			len = strlen(server) + strlen(db->treename) + 9;
			/* TODO fix leak syncpath and umask unset */
			MALLOC(payload.fileurl, len, RET_ERR(handle, ALPM_ERR_MEMORY, -1));
			snprintf(payload.fileurl, len, "%s/%s.db.sig", server, db->treename);
			payload.handle = handle;
			payload.force = 1;
			payload.errors_ok = (level & ALPM_SIG_DATABASE_OPTIONAL);

			/* set hard upper limit of 16KiB */
			payload.max_size = 16 * 1024;

			sig_ret = _alpm_download(&payload, syncpath, NULL);
			/* errors_ok suppresses error messages, but not the return code */
			sig_ret = payload.errors_ok ? 0 : sig_ret;
			_alpm_dload_payload_reset(&payload);
		}

		if(ret != -1 && sig_ret != -1) {
			break;
		}
	}

	if(ret == 1) {
		/* files match, do nothing */
		handle->pm_errno = 0;
		goto cleanup;
	} else if(ret == -1) {
		/* pm_errno was set by the download code */
		_alpm_log(handle, ALPM_LOG_DEBUG, "failed to sync db: %s\n",
				alpm_strerror(handle->pm_errno));
		goto cleanup;
	}

	/* Cache needs to be rebuilt */
	_alpm_db_free_pkgcache(db);

	/* clear all status flags regarding validity/existence */
	db->status &= ~DB_STATUS_VALID;
	db->status &= ~DB_STATUS_INVALID;
	db->status &= ~DB_STATUS_EXISTS;
	db->status &= ~DB_STATUS_MISSING;

	if(sync_db_validate(db)) {
		/* pm_errno should be set */
		ret = -1;
	}

cleanup:

	if(_alpm_handle_unlock(handle)) {
		_alpm_log(handle, ALPM_LOG_WARNING, _("could not remove lock file %s\n"),
				handle->lockfile);
	}
	free(syncpath);
	umask(oldmask);
	return ret;
}
Exemplo n.º 11
0
static int extract_single_file(alpm_handle_t *handle, struct archive *archive,
		struct archive_entry *entry, alpm_pkg_t *newpkg, alpm_pkg_t *oldpkg)
{
	const char *entryname;
	mode_t entrymode;
	char filename[PATH_MAX]; /* the actual file we're extracting */
	int needbackup = 0, notouch = 0;
	const char *hash_orig = NULL;
	char *entryname_orig = NULL;
	int errors = 0;

	entryname = archive_entry_pathname(entry);
	entrymode = archive_entry_mode(entry);

	if(strcmp(entryname, ".INSTALL") == 0) {
		/* the install script goes inside the db */
		snprintf(filename, PATH_MAX, "%s%s-%s/install",
				_alpm_db_path(handle->db_local), newpkg->name, newpkg->version);
		archive_entry_set_perm(entry, 0644);
	} else if(strcmp(entryname, ".CHANGELOG") == 0) {
		/* the changelog goes inside the db */
		snprintf(filename, PATH_MAX, "%s%s-%s/changelog",
				_alpm_db_path(handle->db_local), newpkg->name, newpkg->version);
		archive_entry_set_perm(entry, 0644);
	} else if(strcmp(entryname, ".MTREE") == 0) {
		/* the mtree file goes inside the db */
		snprintf(filename, PATH_MAX, "%s%s-%s/mtree",
				_alpm_db_path(handle->db_local), newpkg->name, newpkg->version);
		archive_entry_set_perm(entry, 0644);
	} else if(*entryname == '.') {
		/* for now, ignore all files starting with '.' that haven't
		 * already been handled (for future possibilities) */
		_alpm_log(handle, ALPM_LOG_DEBUG, "skipping extraction of '%s'\n", entryname);
		archive_read_data_skip(archive);
		return 0;
	} else {
		/* build the new entryname relative to handle->root */
		snprintf(filename, PATH_MAX, "%s%s", handle->root, entryname);
	}

	/* if a file is in NoExtract then we never extract it */
	if(_alpm_fnmatch_patterns(handle->noextract, entryname) == 0) {
		_alpm_log(handle, ALPM_LOG_DEBUG, "%s is in NoExtract,"
				" skipping extraction of %s\n",
				entryname, filename);
		alpm_logaction(handle, ALPM_CALLER_PREFIX,
				"note: %s is in NoExtract, skipping extraction\n", entryname);
		archive_read_data_skip(archive);
		return 0;
	}

	/* Check for file existence. This is one of the more crucial parts
	 * to get 'right'. Here are the possibilities, with the filesystem
	 * on the left and the package on the top:
	 * (F=file, N=node, S=symlink, D=dir)
	 *               |  F/N  |   D
	 *  non-existent |   1   |   2
	 *  F/N          |   3   |   4
	 *  D            |   5   |   6
	 *
	 *  1,2- extract, no magic necessary. lstat (_alpm_lstat) will fail here.
	 *  3,4- conflict checks should have caught this. either overwrite
	 *      or backup the file.
	 *  5- file replacing directory- don't allow it.
	 *  6- skip extraction, dir already exists.
	 */

	struct stat lsbuf;
	if(_alpm_lstat(filename, &lsbuf) != 0) {
		/* cases 1,2: file doesn't exist, skip all backup checks */
	} else {
		if(S_ISDIR(lsbuf.st_mode)) {
			if(S_ISDIR(entrymode)) {
				uid_t entryuid = archive_entry_uid(entry);
				gid_t entrygid = archive_entry_gid(entry);

				/* case 6: existing dir, ignore it */
				if(lsbuf.st_mode != entrymode) {
					/* if filesystem perms are different than pkg perms, warn user */
					mode_t mask = 07777;
					_alpm_log(handle, ALPM_LOG_WARNING, _("directory permissions differ on %s\n"
							"filesystem: %o  package: %o\n"), filename, lsbuf.st_mode & mask,
							entrymode & mask);
					alpm_logaction(handle, ALPM_CALLER_PREFIX,
							"warning: directory permissions differ on %s\n"
							"filesystem: %o  package: %o\n", filename, lsbuf.st_mode & mask,
							entrymode & mask);
				}

				if((entryuid != lsbuf.st_uid) || (entrygid != lsbuf.st_gid)) {
					_alpm_log(handle, ALPM_LOG_WARNING, _("directory ownership differs on %s\n"
							"filesystem: %u:%u  package: %u:%u\n"), filename,
							lsbuf.st_uid, lsbuf.st_gid, entryuid, entrygid);
					alpm_logaction(handle, ALPM_CALLER_PREFIX,
							"warning: directory ownership differs on %s\n"
							"filesystem: %u:%u  package: %u:%u\n", filename,
							lsbuf.st_uid, lsbuf.st_gid, entryuid, entrygid);
				}

				_alpm_log(handle, ALPM_LOG_DEBUG, "extract: skipping dir extraction of %s\n",
						filename);
				archive_read_data_skip(archive);
				return 0;
			} else {
				/* case 5: trying to overwrite dir with file, don't allow it */
				_alpm_log(handle, ALPM_LOG_ERROR, _("extract: not overwriting dir with file %s\n"),
						filename);
				archive_read_data_skip(archive);
				return 1;
			}
		} else if(S_ISDIR(entrymode)) {
			/* case 4: trying to overwrite file with dir */
			_alpm_log(handle, ALPM_LOG_DEBUG, "extract: overwriting file with dir %s\n",
					filename);
		} else {
			/* case 3: */
			/* if file is in NoUpgrade, don't touch it */
			if(_alpm_fnmatch_patterns(handle->noupgrade, entryname) == 0) {
				notouch = 1;
			} else {
				alpm_backup_t *backup;
				/* go to the backup array and see if our conflict is there */
				/* check newpkg first, so that adding backup files is retroactive */
				backup = _alpm_needbackup(entryname, newpkg);
				if(backup) {
					needbackup = 1;
				}

				/* check oldpkg for a backup entry, store the hash if available */
				if(oldpkg) {
					backup = _alpm_needbackup(entryname, oldpkg);
					if(backup) {
						hash_orig = backup->hash;
						needbackup = 1;
					}
				}
			}
		}
	}

	/* we need access to the original entryname later after calls to
	 * archive_entry_set_pathname(), so we need to dupe it and free() later */
	STRDUP(entryname_orig, entryname, RET_ERR(handle, ALPM_ERR_MEMORY, -1));

	if(needbackup) {
		char *checkfile;
		char *hash_local = NULL, *hash_pkg = NULL;
		size_t len;

		len = strlen(filename) + 10;
		MALLOC(checkfile, len,
				errors++; handle->pm_errno = ALPM_ERR_MEMORY; goto needbackup_cleanup);
		snprintf(checkfile, len, "%s.paccheck", filename);

		if(perform_extraction(handle, archive, entry, checkfile, entryname_orig)) {
			errors++;
			goto needbackup_cleanup;
		}

		hash_local = alpm_compute_md5sum(filename);
		hash_pkg = alpm_compute_md5sum(checkfile);

		/* update the md5 hash in newpkg's backup (it will be the new original) */
		alpm_list_t *i;
		for(i = alpm_pkg_get_backup(newpkg); i; i = i->next) {
			alpm_backup_t *backup = i->data;
			char *newhash;
			if(!backup->name || strcmp(backup->name, entryname_orig) != 0) {
				continue;
			}
			STRDUP(newhash, hash_pkg, RET_ERR(handle, ALPM_ERR_MEMORY, -1));
			FREE(backup->hash);
			backup->hash = newhash;
		}

		_alpm_log(handle, ALPM_LOG_DEBUG, "checking hashes for %s\n", entryname_orig);
		_alpm_log(handle, ALPM_LOG_DEBUG, "current:  %s\n", hash_local);
		_alpm_log(handle, ALPM_LOG_DEBUG, "new:      %s\n", hash_pkg);
		_alpm_log(handle, ALPM_LOG_DEBUG, "original: %s\n", hash_orig);

		if(hash_local && hash_pkg && strcmp(hash_local, hash_pkg) == 0) {
			/* local and new files are the same, updating anyway to get
			 * correct timestamps */
			_alpm_log(handle, ALPM_LOG_DEBUG, "action: installing new file: %s\n",
					entryname_orig);
			if(try_rename(handle, checkfile, filename)) {
				errors++;
			}
		} else if(hash_orig && hash_pkg && strcmp(hash_orig, hash_pkg) == 0) {
			/* original and new files are the same, leave the local version alone,
			 * including any user changes */
			_alpm_log(handle, ALPM_LOG_DEBUG,
					"action: leaving existing file in place\n");
			unlink(checkfile);
		} else if(hash_orig && hash_local && strcmp(hash_orig, hash_local) == 0) {
			/* installed file has NOT been changed by user,
			 * update to the new version */
			_alpm_log(handle, ALPM_LOG_DEBUG, "action: installing new file: %s\n",
					entryname_orig);
			if(try_rename(handle, checkfile, filename)) {
				errors++;
			}
		} else {
			/* none of the three files matched another, unpack the new file alongside
			 * the local file */

			if(oldpkg) {
				char *newpath;
				size_t newlen = strlen(filename) + strlen(".pacnew") + 1;

				_alpm_log(handle, ALPM_LOG_DEBUG,
						"action: keeping current file and installing"
						" new one with .pacnew ending\n");

				MALLOC(newpath, newlen,
						errors++; handle->pm_errno = ALPM_ERR_MEMORY; goto needbackup_cleanup);
				snprintf(newpath, newlen, "%s.pacnew", filename);

				if(try_rename(handle, checkfile, newpath)) {
					errors++;
				} else {
					_alpm_log(handle, ALPM_LOG_WARNING, _("%s installed as %s\n"),
							filename, newpath);
					alpm_logaction(handle, ALPM_CALLER_PREFIX,
							"warning: %s installed as %s\n", filename, newpath);
				}

				free(newpath);
			} else {
				char *newpath;
				size_t newlen = strlen(filename) + strlen(".pacorig") + 1;

				_alpm_log(handle, ALPM_LOG_DEBUG,
						"action: saving existing file with a .pacorig ending"
						" and installing a new one\n");

				MALLOC(newpath, newlen,
						errors++; handle->pm_errno = ALPM_ERR_MEMORY; goto needbackup_cleanup);
				snprintf(newpath, newlen, "%s.pacorig", filename);

				/* move the existing file to the "pacorig" */
				if(try_rename(handle, filename, newpath)) {
					errors++;   /* failed rename filename  -> filename.pacorig */
					errors++;   /* failed rename checkfile -> filename */
				} else {
					/* rename the file we extracted to the real name */
					if(try_rename(handle, checkfile, filename)) {
						errors++;
					} else {
						_alpm_log(handle, ALPM_LOG_WARNING,
								_("%s saved as %s\n"), filename, newpath);
						alpm_logaction(handle, ALPM_CALLER_PREFIX,
								"warning: %s saved as %s\n", filename, newpath);
					}
				}

				free(newpath);
			}
		}

needbackup_cleanup:
		free(checkfile);
		free(hash_local);
		free(hash_pkg);
	} else {
Exemplo n.º 12
0
static int sync_db_populate(alpm_db_t *db)
{
	const char *dbpath;
	size_t est_count;
	int count = 0;
	struct stat buf;
	struct archive *archive;
	struct archive_entry *entry;
	alpm_pkg_t *pkg = NULL;

	if(db->status & DB_STATUS_INVALID) {
		RET_ERR(db->handle, ALPM_ERR_DB_INVALID, -1);
	}
	if(db->status & DB_STATUS_MISSING) {
		RET_ERR(db->handle, ALPM_ERR_DB_NOT_FOUND, -1);
	}

	if((archive = archive_read_new()) == NULL) {
		RET_ERR(db->handle, ALPM_ERR_LIBARCHIVE, -1);
	}

	archive_read_support_compression_all(archive);
	archive_read_support_format_all(archive);

	dbpath = _alpm_db_path(db);
	if(!dbpath) {
		/* pm_errno set in _alpm_db_path() */
		return -1;
	}

	_alpm_log(db->handle, ALPM_LOG_DEBUG, "opening database archive %s\n", dbpath);

	if(archive_read_open_filename(archive, dbpath,
				ARCHIVE_DEFAULT_BYTES_PER_BLOCK) != ARCHIVE_OK) {
		_alpm_log(db->handle, ALPM_LOG_ERROR, _("could not open file %s: %s\n"), dbpath,
				archive_error_string(archive));
		archive_read_finish(archive);
		RET_ERR(db->handle, ALPM_ERR_DB_OPEN, -1);
	}
	if(stat(dbpath, &buf) != 0) {
		RET_ERR(db->handle, ALPM_ERR_DB_OPEN, -1);
	}
	est_count = estimate_package_count(&buf, archive);

	/* initialize hash at 66% full */
	db->pkgcache = _alpm_pkghash_create(est_count * 3 / 2);
	if(db->pkgcache == NULL) {
		RET_ERR(db->handle, ALPM_ERR_MEMORY, -1);
	}

	while(archive_read_next_header(archive, &entry) == ARCHIVE_OK) {
		mode_t mode = archive_entry_mode(entry);
		if(S_ISDIR(mode)) {
			continue;
		} else {
			/* we have desc, depends or deltas - parse it */
			if(sync_db_read(db, archive, entry, &pkg) != 0) {
				_alpm_log(db->handle, ALPM_LOG_ERROR,
						_("could not parse package description file '%s' from db '%s'\n"),
						archive_entry_pathname(entry), db->treename);
				continue;
			}
		}
	}

	count = alpm_list_count(db->pkgcache->list);

	if(count > 0) {
		db->pkgcache->list = alpm_list_msort(db->pkgcache->list, (size_t)count, _alpm_pkg_cmp);
	}
	archive_read_finish(archive);
	_alpm_log(db->handle, ALPM_LOG_DEBUG, "added %d packages to package cache for db '%s'\n",
			count, db->treename);

	return count;
}