2 * be_package.c : backend for packages
4 * Copyright (c) 2006-2012 Pacman Development Team <pacman-dev@archlinux.org>
5 * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org>
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.
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.
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/>.
24 #include <sys/types.h>
30 #include <archive_entry.h>
33 #include "alpm_list.h"
42 struct package_changelog
{
43 struct archive
*archive
;
48 * Open a package changelog for reading. Similar to fopen in functionality,
49 * except that the returned 'file stream' is from an archive.
50 * @param pkg the package (file) to read the changelog
51 * @return a 'file stream' to the package changelog
53 static void *_package_changelog_open(alpm_pkg_t
*pkg
)
55 ASSERT(pkg
!= NULL
, return NULL
);
57 struct package_changelog
*changelog
;
58 struct archive
*archive
;
59 struct archive_entry
*entry
;
60 const char *pkgfile
= pkg
->origin_data
.file
;
64 fd
= _alpm_open_archive(pkg
->handle
, pkgfile
, &buf
,
65 &archive
, ALPM_ERR_PKG_OPEN
);
70 while(archive_read_next_header(archive
, &entry
) == ARCHIVE_OK
) {
71 const char *entry_name
= archive_entry_pathname(entry
);
73 if(strcmp(entry_name
, ".CHANGELOG") == 0) {
74 changelog
= malloc(sizeof(struct package_changelog
));
76 pkg
->handle
->pm_errno
= ALPM_ERR_MEMORY
;
77 archive_read_finish(archive
);
81 changelog
->archive
= archive
;
86 /* we didn't find a changelog */
87 archive_read_finish(archive
);
95 * Read data from an open changelog 'file stream'. Similar to fread in
96 * functionality, this function takes a buffer and amount of data to read.
97 * @param ptr a buffer to fill with raw changelog data
98 * @param size the size of the buffer
99 * @param pkg the package that the changelog is being read from
100 * @param fp a 'file stream' to the package changelog
101 * @return the number of characters read, or 0 if there is no more data
103 static size_t _package_changelog_read(void *ptr
, size_t size
,
104 const alpm_pkg_t UNUSED
*pkg
, void *fp
)
106 struct package_changelog
*changelog
= fp
;
107 ssize_t sret
= archive_read_data(changelog
->archive
, ptr
, size
);
108 /* Report error (negative values) */
110 RET_ERR(pkg
->handle
, ALPM_ERR_LIBARCHIVE
, 0);
117 * Close a package changelog for reading. Similar to fclose in functionality,
118 * except that the 'file stream' is from an archive.
119 * @param pkg the package (file) that the changelog was read from
120 * @param fp a 'file stream' to the package changelog
121 * @return whether closing the package changelog stream was successful
123 static int _package_changelog_close(const alpm_pkg_t UNUSED
*pkg
, void *fp
)
126 struct package_changelog
*changelog
= fp
;
127 ret
= archive_read_finish(changelog
->archive
);
128 CLOSE(changelog
->fd
);
133 /** Package file operations struct accessor. We implement this as a method
134 * rather than a static struct as in be_files because we want to reuse the
135 * majority of the default_pkg_ops struct and add only a few operations of
138 static struct pkg_operations
*get_file_pkg_ops(void)
140 static struct pkg_operations file_pkg_ops
;
141 static int file_pkg_ops_initialized
= 0;
142 if(!file_pkg_ops_initialized
) {
143 file_pkg_ops
= default_pkg_ops
;
144 file_pkg_ops
.changelog_open
= _package_changelog_open
;
145 file_pkg_ops
.changelog_read
= _package_changelog_read
;
146 file_pkg_ops
.changelog_close
= _package_changelog_close
;
147 file_pkg_ops_initialized
= 1;
149 return &file_pkg_ops
;
153 * Parses the package description file for a package into a alpm_pkg_t struct.
154 * @param archive the archive to read from, pointed at the .PKGINFO entry
155 * @param newpkg an empty alpm_pkg_t struct to fill with package info
157 * @return 0 on success, -1 on error
159 static int parse_descfile(alpm_handle_t
*handle
, struct archive
*a
, alpm_pkg_t
*newpkg
)
163 int ret
, linenum
= 0;
164 struct archive_read_buffer buf
;
166 memset(&buf
, 0, sizeof(buf
));
167 /* 512K for a line length seems reasonable */
168 buf
.max_line_size
= 512 * 1024;
170 /* loop until we reach EOF or other error */
171 while((ret
= _alpm_archive_fgets(a
, &buf
)) == ARCHIVE_OK
) {
172 size_t len
= _alpm_strip_newline(buf
.line
, buf
.real_line_size
);
176 if(len
== 0 || key
[0] == '#') {
179 /* line is always in this format: "key = value"
180 * we can be sure the " = " exists, so look for that */
181 ptr
= memchr(key
, ' ', len
);
182 if(!ptr
|| (size_t)(ptr
- key
+ 2) > len
|| memcmp(ptr
, " = ", 3) != 0) {
183 _alpm_log(handle
, ALPM_LOG_DEBUG
,
184 "%s: syntax error in description file line %d\n",
185 newpkg
->name
? newpkg
->name
: "error", linenum
);
187 /* NULL the end of the key portion, move ptr to start of value */
190 if(strcmp(key
, "pkgname") == 0) {
191 STRDUP(newpkg
->name
, ptr
, return -1);
192 newpkg
->name_hash
= _alpm_hash_sdbm(newpkg
->name
);
193 } else if(strcmp(key
, "pkgbase") == 0) {
195 } else if(strcmp(key
, "pkgver") == 0) {
196 STRDUP(newpkg
->version
, ptr
, return -1);
197 } else if(strcmp(key
, "pkgdesc") == 0) {
198 STRDUP(newpkg
->desc
, ptr
, return -1);
199 } else if(strcmp(key
, "group") == 0) {
200 newpkg
->groups
= alpm_list_add(newpkg
->groups
, strdup(ptr
));
201 } else if(strcmp(key
, "url") == 0) {
202 STRDUP(newpkg
->url
, ptr
, return -1);
203 } else if(strcmp(key
, "license") == 0) {
204 newpkg
->licenses
= alpm_list_add(newpkg
->licenses
, strdup(ptr
));
205 } else if(strcmp(key
, "builddate") == 0) {
206 newpkg
->builddate
= _alpm_parsedate(ptr
);
207 } else if(strcmp(key
, "packager") == 0) {
208 STRDUP(newpkg
->packager
, ptr
, return -1);
209 } else if(strcmp(key
, "arch") == 0) {
210 STRDUP(newpkg
->arch
, ptr
, return -1);
211 } else if(strcmp(key
, "size") == 0) {
212 /* size in the raw package is uncompressed (installed) size */
213 newpkg
->isize
= _alpm_strtoofft(ptr
);
214 } else if(strcmp(key
, "depend") == 0) {
215 alpm_depend_t
*dep
= _alpm_splitdep(ptr
);
216 newpkg
->depends
= alpm_list_add(newpkg
->depends
, dep
);
217 } else if(strcmp(key
, "optdepend") == 0) {
218 alpm_depend_t
*optdep
= _alpm_splitdep(ptr
);
219 newpkg
->optdepends
= alpm_list_add(newpkg
->optdepends
, optdep
);
220 } else if(strcmp(key
, "conflict") == 0) {
221 alpm_depend_t
*conflict
= _alpm_splitdep(ptr
);
222 newpkg
->conflicts
= alpm_list_add(newpkg
->conflicts
, conflict
);
223 } else if(strcmp(key
, "replaces") == 0) {
224 alpm_depend_t
*replace
= _alpm_splitdep(ptr
);
225 newpkg
->replaces
= alpm_list_add(newpkg
->replaces
, replace
);
226 } else if(strcmp(key
, "provides") == 0) {
227 alpm_depend_t
*provide
= _alpm_splitdep(ptr
);
228 newpkg
->provides
= alpm_list_add(newpkg
->provides
, provide
);
229 } else if(strcmp(key
, "backup") == 0) {
230 alpm_backup_t
*backup
;
231 CALLOC(backup
, 1, sizeof(alpm_backup_t
), return -1);
232 STRDUP(backup
->name
, ptr
, return -1);
233 newpkg
->backup
= alpm_list_add(newpkg
->backup
, backup
);
234 } else if(strcmp(key
, "force") == 0) {
235 /* deprecated, skip it */
236 } else if(strcmp(key
, "makepkgopt") == 0) {
239 _alpm_log(handle
, ALPM_LOG_DEBUG
, "%s: unknown key '%s' in description file line %d\n",
240 newpkg
->name
? newpkg
->name
: "error", key
, linenum
);
244 if(ret
!= ARCHIVE_EOF
) {
245 _alpm_log(handle
, ALPM_LOG_DEBUG
, "error parsing package descfile\n");
253 * Validate a package.
254 * @param handle the context handle
255 * @param pkgfile path to the package file
256 * @param syncpkg package object to load verification data from (md5sum,
257 * sha256sum, and/or base64 signature)
258 * @param level the required level of signature verification
259 * @param sigdata signature data from the package to pass back
260 * @param validation successful validations performed on the package file
261 * @return 0 if package is fully valid, -1 and pm_errno otherwise
263 int _alpm_pkg_validate_internal(alpm_handle_t
*handle
,
264 const char *pkgfile
, alpm_pkg_t
*syncpkg
, alpm_siglevel_t level
,
265 alpm_siglist_t
**sigdata
, alpm_pkgvalidation_t
*validation
)
268 handle
->pm_errno
= 0;
270 if(pkgfile
== NULL
|| strlen(pkgfile
) == 0) {
271 RET_ERR(handle
, ALPM_ERR_WRONG_ARGS
, -1);
274 /* attempt to access the package file, ensure it exists */
275 if(_alpm_access(handle
, NULL
, pkgfile
, R_OK
) != 0) {
276 if(errno
== ENOENT
) {
277 handle
->pm_errno
= ALPM_ERR_PKG_NOT_FOUND
;
278 } else if(errno
== EACCES
) {
279 handle
->pm_errno
= ALPM_ERR_BADPERMS
;
281 handle
->pm_errno
= ALPM_ERR_PKG_OPEN
;
286 /* can we get away with skipping checksums? */
288 if(level
& ALPM_SIG_PACKAGE
) {
289 if(syncpkg
&& syncpkg
->base64_sig
) {
292 char *sigpath
= _alpm_sigpath(handle
, pkgfile
);
293 if(sigpath
&& !_alpm_access(handle
, NULL
, sigpath
, R_OK
)) {
300 if(syncpkg
&& !has_sig
) {
301 if(syncpkg
->md5sum
&& !syncpkg
->sha256sum
) {
302 _alpm_log(handle
, ALPM_LOG_DEBUG
, "md5sum: %s\n", syncpkg
->md5sum
);
303 _alpm_log(handle
, ALPM_LOG_DEBUG
, "checking md5sum for %s\n", pkgfile
);
304 if(_alpm_test_checksum(pkgfile
, syncpkg
->md5sum
, ALPM_PKG_VALIDATION_MD5SUM
) != 0) {
305 RET_ERR(handle
, ALPM_ERR_PKG_INVALID_CHECKSUM
, -1);
308 *validation
|= ALPM_PKG_VALIDATION_MD5SUM
;
312 if(syncpkg
->sha256sum
) {
313 _alpm_log(handle
, ALPM_LOG_DEBUG
, "sha256sum: %s\n", syncpkg
->sha256sum
);
314 _alpm_log(handle
, ALPM_LOG_DEBUG
, "checking sha256sum for %s\n", pkgfile
);
315 if(_alpm_test_checksum(pkgfile
, syncpkg
->sha256sum
, ALPM_PKG_VALIDATION_SHA256SUM
) != 0) {
316 RET_ERR(handle
, ALPM_ERR_PKG_INVALID_CHECKSUM
, -1);
319 *validation
|= ALPM_PKG_VALIDATION_SHA256SUM
;
324 /* even if we don't have a sig, run the check code if level tells us to */
325 if(has_sig
|| level
& ALPM_SIG_PACKAGE
) {
326 const char *sig
= syncpkg
? syncpkg
->base64_sig
: NULL
;
327 _alpm_log(handle
, ALPM_LOG_DEBUG
, "sig data: %s\n", sig
? sig
: "<from .sig>");
328 if(_alpm_check_pgp_helper(handle
, pkgfile
, sig
,
329 level
& ALPM_SIG_PACKAGE_OPTIONAL
, level
& ALPM_SIG_PACKAGE_MARGINAL_OK
,
330 level
& ALPM_SIG_PACKAGE_UNKNOWN_OK
, sigdata
)) {
331 handle
->pm_errno
= ALPM_ERR_PKG_INVALID_SIG
;
334 if(validation
&& has_sig
) {
335 *validation
|= ALPM_PKG_VALIDATION_SIGNATURE
;
339 if (validation
&& !*validation
) {
340 *validation
= ALPM_PKG_VALIDATION_NONE
;
347 * Load a package and create the corresponding alpm_pkg_t struct.
348 * @param handle the context handle
349 * @param pkgfile path to the package file
350 * @param full whether to stop the load after metadata is read or continue
351 * through the full archive
353 alpm_pkg_t
*_alpm_pkg_load_internal(alpm_handle_t
*handle
,
354 const char *pkgfile
, int full
)
356 int ret
, fd
, config
= 0;
357 struct archive
*archive
;
358 struct archive_entry
*entry
;
361 size_t files_size
= 0;
363 if(pkgfile
== NULL
|| strlen(pkgfile
) == 0) {
364 RET_ERR(handle
, ALPM_ERR_WRONG_ARGS
, NULL
);
367 fd
= _alpm_open_archive(handle
, pkgfile
, &st
, &archive
, ALPM_ERR_PKG_OPEN
);
369 if(errno
== ENOENT
) {
370 handle
->pm_errno
= ALPM_ERR_PKG_NOT_FOUND
;
371 } else if(errno
== EACCES
) {
372 handle
->pm_errno
= ALPM_ERR_BADPERMS
;
374 handle
->pm_errno
= ALPM_ERR_PKG_OPEN
;
379 newpkg
= _alpm_pkg_new();
381 handle
->pm_errno
= ALPM_ERR_MEMORY
;
384 STRDUP(newpkg
->filename
, pkgfile
,
385 handle
->pm_errno
= ALPM_ERR_MEMORY
; goto error
);
386 newpkg
->size
= st
.st_size
;
388 _alpm_log(handle
, ALPM_LOG_DEBUG
, "starting package load for %s\n", pkgfile
);
390 /* If full is false, only read through the archive until we find our needed
391 * metadata. If it is true, read through the entire archive, which serves
392 * as a verfication of integrity and allows us to create the filelist. */
393 while((ret
= archive_read_next_header(archive
, &entry
)) == ARCHIVE_OK
) {
394 const char *entry_name
= archive_entry_pathname(entry
);
396 if(strcmp(entry_name
, ".PKGINFO") == 0) {
397 /* parse the info file */
398 if(parse_descfile(handle
, archive
, newpkg
) != 0) {
399 _alpm_log(handle
, ALPM_LOG_ERROR
, _("could not parse package description file in %s\n"),
403 if(newpkg
->name
== NULL
|| strlen(newpkg
->name
) == 0) {
404 _alpm_log(handle
, ALPM_LOG_ERROR
, _("missing package name in %s\n"), pkgfile
);
407 if(newpkg
->version
== NULL
|| strlen(newpkg
->version
) == 0) {
408 _alpm_log(handle
, ALPM_LOG_ERROR
, _("missing package version in %s\n"), pkgfile
);
413 } else if(strcmp(entry_name
, ".INSTALL") == 0) {
414 newpkg
->scriptlet
= 1;
415 } else if(*entry_name
== '.') {
416 /* for now, ignore all files starting with '.' that haven't
417 * already been handled (for future possibilities) */
419 const size_t files_count
= newpkg
->files
.count
;
420 alpm_file_t
*current_file
;
421 /* Keep track of all files for filelist generation */
422 if(files_count
>= files_size
) {
423 size_t old_size
= files_size
;
424 alpm_file_t
*newfiles
;
425 if(files_size
== 0) {
430 newfiles
= realloc(newpkg
->files
.files
,
431 sizeof(alpm_file_t
) * files_size
);
433 _alpm_alloc_fail(sizeof(alpm_file_t
) * files_size
);
436 /* ensure all new memory is zeroed out, in both the initial
437 * allocation and later reallocs */
438 memset(newfiles
+ old_size
, 0,
439 sizeof(alpm_file_t
) * (files_size
- old_size
));
440 newpkg
->files
.files
= newfiles
;
442 current_file
= newpkg
->files
.files
+ files_count
;
443 STRDUP(current_file
->name
, entry_name
, goto error
);
444 current_file
->size
= archive_entry_size(entry
);
445 current_file
->mode
= archive_entry_mode(entry
);
446 newpkg
->files
.count
++;
449 if(archive_read_data_skip(archive
)) {
450 _alpm_log(handle
, ALPM_LOG_ERROR
, _("error while reading package %s: %s\n"),
451 pkgfile
, archive_error_string(archive
));
452 handle
->pm_errno
= ALPM_ERR_LIBARCHIVE
;
456 /* if we are not doing a full read, see if we have all we need */
457 if(!full
&& config
) {
462 if(ret
!= ARCHIVE_EOF
&& ret
!= ARCHIVE_OK
) { /* An error occured */
463 _alpm_log(handle
, ALPM_LOG_ERROR
, _("error while reading package %s: %s\n"),
464 pkgfile
, archive_error_string(archive
));
465 handle
->pm_errno
= ALPM_ERR_LIBARCHIVE
;
470 _alpm_log(handle
, ALPM_LOG_ERROR
, _("missing package metadata in %s\n"), pkgfile
);
474 archive_read_finish(archive
);
477 /* internal fields for package struct */
478 newpkg
->origin
= ALPM_PKG_FROM_FILE
;
479 newpkg
->origin_data
.file
= strdup(pkgfile
);
480 newpkg
->ops
= get_file_pkg_ops();
481 newpkg
->handle
= handle
;
482 newpkg
->infolevel
= INFRQ_BASE
| INFRQ_DESC
| INFRQ_SCRIPTLET
;
483 newpkg
->validation
= ALPM_PKG_VALIDATION_NONE
;
486 if(newpkg
->files
.files
) {
487 /* attempt to hand back any memory we don't need */
488 newpkg
->files
.files
= realloc(newpkg
->files
.files
,
489 sizeof(alpm_file_t
) * newpkg
->files
.count
);
490 /* "checking for conflicts" requires a sorted list, ensure that here */
491 _alpm_log(handle
, ALPM_LOG_DEBUG
,
492 "sorting package filelist for %s\n", pkgfile
);
494 qsort(newpkg
->files
.files
, newpkg
->files
.count
,
495 sizeof(alpm_file_t
), _alpm_files_cmp
);
497 newpkg
->infolevel
|= INFRQ_FILES
;
503 handle
->pm_errno
= ALPM_ERR_PKG_INVALID
;
505 _alpm_pkg_free(newpkg
);
506 archive_read_finish(archive
);
514 int SYMEXPORT
alpm_pkg_load(alpm_handle_t
*handle
, const char *filename
, int full
,
515 alpm_siglevel_t level
, alpm_pkg_t
**pkg
)
517 alpm_pkgvalidation_t validation
= 0;
519 CHECK_HANDLE(handle
, return -1);
520 ASSERT(pkg
!= NULL
, RET_ERR(handle
, ALPM_ERR_WRONG_ARGS
, -1));
522 if(_alpm_pkg_validate_internal(handle
, filename
, NULL
, level
, NULL
,
523 &validation
) == -1) {
524 /* pm_errno is set by pkg_validate */
527 *pkg
= _alpm_pkg_load_internal(handle
, filename
, full
);
529 /* pm_errno is set by pkg_load */
532 (*pkg
)->validation
= validation
;
537 /* vim: set ts=2 sw=2 noet: */