source: branches/stable/mindi-busybox/archival/dpkg.c @ 821

Last change on this file since 821 was 821, checked in by bruno, 13 years ago

Addition of busybox 1.2.1 as a mindi-busybox new package
This should avoid delivering binary files in mindi not built there (Fedora and Debian are quite serious about that)

File size: 52.6 KB
Line 
1/*
2 *  Mini dpkg implementation for busybox.
3 *  This is not meant as a replacement for dpkg
4 *
5 *  Written By Glenn McGrath with the help of others
6 *  Copyright (C) 2001 by Glenn McGrath
7 *
8 *  Started life as a busybox implementation of udpkg
9 *
10 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
11 */
12
13/*
14 * Known difference between busybox dpkg and the official dpkg that i don't
15 * consider important, its worth keeping a note of differences anyway, just to
16 * make it easier to maintain.
17 *  - The first value for the Confflile: field isnt placed on a new line.
18 *  - When installing a package the Status: field is placed at the end of the
19 *      section, rather than just after the Package: field.
20 *
21 * Bugs that need to be fixed
22 *  - (unknown, please let me know when you find any)
23 *
24 */
25
26#include <fcntl.h>
27#include <getopt.h>
28#include <stdlib.h>
29#include <string.h>
30#include <unistd.h>
31#include "unarchive.h"
32#include "busybox.h"
33
34/* NOTE: If you vary HASH_PRIME sizes be aware,
35 * 1) Tweaking these will have a big effect on how much memory this program uses.
36 * 2) For computational efficiency these hash tables should be at least 20%
37 *    larger than the maximum number of elements stored in it.
38 * 3) All _HASH_PRIME's must be a prime number or chaos is assured, if your looking
39 *    for a prime, try http://www.utm.edu/research/primes/lists/small/10000.txt
40 * 4) If you go bigger than 15 bits you may get into trouble (untested) as its
41 *    sometimes cast to an unsigned int, if you go to 16 bit you will overlap
42 *    int's and chaos is assured, 16381 is the max prime for 14 bit field
43 */
44
45/* NAME_HASH_PRIME, Stores package names and versions,
46 * I estimate it should be at least 50% bigger than PACKAGE_HASH_PRIME,
47 * as there a lot of duplicate version numbers */
48#define NAME_HASH_PRIME 16381
49static char *name_hashtable[NAME_HASH_PRIME + 1];
50
51/* PACKAGE_HASH_PRIME, Maximum number of unique packages,
52 * It must not be smaller than STATUS_HASH_PRIME,
53 * Currently only packages from status_hashtable are stored in here, but in
54 * future this may be used to store packages not only from a status file,
55 * but an available_hashtable, and even multiple packages files.
56 * Package can be stored more than once if they have different versions.
57 * e.g. The same package may have different versions in the status file
58 *      and available file */
59#define PACKAGE_HASH_PRIME 10007
60typedef struct edge_s {
61    unsigned int operator:3;
62    unsigned int type:4;
63    unsigned int name:14;
64    unsigned int version:14;
65} edge_t;
66
67typedef struct common_node_s {
68    unsigned int name:14;
69    unsigned int version:14;
70    unsigned int num_of_edges:14;
71    edge_t **edge;
72} common_node_t;
73static common_node_t *package_hashtable[PACKAGE_HASH_PRIME + 1];
74
75/* Currently it doesnt store packages that have state-status of not-installed
76 * So it only really has to be the size of the maximum number of packages
77 * likely to be installed at any one time, so there is a bit of leeway here */
78#define STATUS_HASH_PRIME 8191
79typedef struct status_node_s {
80    unsigned int package:14;    /* has to fit PACKAGE_HASH_PRIME */
81    unsigned int status:14;     /* has to fit STATUS_HASH_PRIME */
82} status_node_t;
83static status_node_t *status_hashtable[STATUS_HASH_PRIME + 1];
84
85/* Even numbers are for 'extras', like ored dependencies or null */
86enum edge_type_e {
87    EDGE_NULL = 0,
88    EDGE_PRE_DEPENDS = 1,
89    EDGE_OR_PRE_DEPENDS = 2,
90    EDGE_DEPENDS = 3,
91    EDGE_OR_DEPENDS = 4,
92    EDGE_REPLACES = 5,
93    EDGE_PROVIDES = 7,
94    EDGE_CONFLICTS = 9,
95    EDGE_SUGGESTS = 11,
96    EDGE_RECOMMENDS = 13,
97    EDGE_ENHANCES = 15
98};
99enum operator_e {
100    VER_NULL = 0,
101    VER_EQUAL = 1,
102    VER_LESS = 2,
103    VER_LESS_EQUAL = 3,
104    VER_MORE = 4,
105    VER_MORE_EQUAL = 5,
106    VER_ANY = 6
107};
108
109enum dpkg_opt_e {
110    dpkg_opt_purge = 1,
111    dpkg_opt_remove = 2,
112    dpkg_opt_unpack = 4,
113    dpkg_opt_configure = 8,
114    dpkg_opt_install = 16,
115    dpkg_opt_package_name = 32,
116    dpkg_opt_filename = 64,
117    dpkg_opt_list_installed = 128,
118    dpkg_opt_force_ignore_depends = 256
119};
120
121typedef struct deb_file_s {
122    char *control_file;
123    char *filename;
124    unsigned int package:14;
125} deb_file_t;
126
127
128static void make_hash(const char *key, unsigned int *start, unsigned int *decrement, const int hash_prime)
129{
130    unsigned long int hash_num = key[0];
131    int len = strlen(key);
132    int i;
133
134    /* Maybe i should have uses a "proper" hashing algorithm here instead
135     * of making one up myself, seems to be working ok though. */
136    for(i = 1; i < len; i++) {
137        /* shifts the ascii based value and adds it to previous value
138         * shift amount is mod 24 because long int is 32 bit and data
139         * to be shifted is 8, don't want to shift data to where it has
140         * no effect*/
141        hash_num += ((key[i] + key[i-1]) << ((key[i] * i) % 24));
142    }
143    *start = (unsigned int) hash_num % hash_prime;
144    *decrement = (unsigned int) 1 + (hash_num % (hash_prime - 1));
145}
146
147/* this adds the key to the hash table */
148static int search_name_hashtable(const char *key)
149{
150    unsigned int probe_address = 0;
151    unsigned int probe_decrement = 0;
152//  char *temp;
153
154    make_hash(key, &probe_address, &probe_decrement, NAME_HASH_PRIME);
155    while(name_hashtable[probe_address] != NULL) {
156        if (strcmp(name_hashtable[probe_address], key) == 0) {
157            return(probe_address);
158        } else {
159            probe_address -= probe_decrement;
160            if ((int)probe_address < 0) {
161                probe_address += NAME_HASH_PRIME;
162            }
163        }
164    }
165    name_hashtable[probe_address] = bb_xstrdup(key);
166    return(probe_address);
167}
168
169/* this DOESNT add the key to the hashtable
170 * TODO make it consistent with search_name_hashtable
171 */
172static unsigned int search_status_hashtable(const char *key)
173{
174    unsigned int probe_address = 0;
175    unsigned int probe_decrement = 0;
176
177    make_hash(key, &probe_address, &probe_decrement, STATUS_HASH_PRIME);
178    while(status_hashtable[probe_address] != NULL) {
179        if (strcmp(key, name_hashtable[package_hashtable[status_hashtable[probe_address]->package]->name]) == 0) {
180            break;
181        } else {
182            probe_address -= probe_decrement;
183            if ((int)probe_address < 0) {
184                probe_address += STATUS_HASH_PRIME;
185            }
186        }
187    }
188    return(probe_address);
189}
190
191/* Need to rethink version comparison, maybe the official dpkg has something i can use ? */
192static int version_compare_part(const char *version1, const char *version2)
193{
194    int upstream_len1 = 0;
195    int upstream_len2 = 0;
196    char *name1_char;
197    char *name2_char;
198    int len1 = 0;
199    int len2 = 0;
200    int tmp_int;
201    int ver_num1;
202    int ver_num2;
203    int ret;
204
205    if (version1 == NULL) {
206        version1 = bb_xstrdup("");
207    }
208    if (version2 == NULL) {
209        version2 = bb_xstrdup("");
210    }
211    upstream_len1 = strlen(version1);
212    upstream_len2 = strlen(version2);
213
214    while ((len1 < upstream_len1) || (len2 < upstream_len2)) {
215        /* Compare non-digit section */
216        tmp_int = strcspn(&version1[len1], "0123456789");
217        name1_char = bb_xstrndup(&version1[len1], tmp_int);
218        len1 += tmp_int;
219        tmp_int = strcspn(&version2[len2], "0123456789");
220        name2_char = bb_xstrndup(&version2[len2], tmp_int);
221        len2 += tmp_int;
222        tmp_int = strcmp(name1_char, name2_char);
223        free(name1_char);
224        free(name2_char);
225        if (tmp_int != 0) {
226            ret = tmp_int;
227            goto cleanup_version_compare_part;
228        }
229
230        /* Compare digits */
231        tmp_int = strspn(&version1[len1], "0123456789");
232        name1_char = bb_xstrndup(&version1[len1], tmp_int);
233        len1 += tmp_int;
234        tmp_int = strspn(&version2[len2], "0123456789");
235        name2_char = bb_xstrndup(&version2[len2], tmp_int);
236        len2 += tmp_int;
237        ver_num1 = atoi(name1_char);
238        ver_num2 = atoi(name2_char);
239        free(name1_char);
240        free(name2_char);
241        if (ver_num1 < ver_num2) {
242            ret = -1;
243            goto cleanup_version_compare_part;
244        }
245        else if (ver_num1 > ver_num2) {
246            ret = 1;
247            goto cleanup_version_compare_part;
248        }
249    }
250    ret = 0;
251cleanup_version_compare_part:
252    return(ret);
253}
254
255/* if ver1 < ver2 return -1,
256 * if ver1 = ver2 return 0,
257 * if ver1 > ver2 return 1,
258 */
259static int version_compare(const unsigned int ver1, const unsigned int ver2)
260{
261    char *ch_ver1 = name_hashtable[ver1];
262    char *ch_ver2 = name_hashtable[ver2];
263
264    char epoch1, epoch2;
265    char *deb_ver1, *deb_ver2;
266    char *ver1_ptr, *ver2_ptr;
267    char *upstream_ver1;
268    char *upstream_ver2;
269    int result;
270
271    /* Compare epoch */
272    if (ch_ver1[1] == ':') {
273        epoch1 = ch_ver1[0];
274        ver1_ptr = strchr(ch_ver1, ':') + 1;
275    } else {
276        epoch1 = '0';
277        ver1_ptr = ch_ver1;
278    }
279    if (ch_ver2[1] == ':') {
280        epoch2 = ch_ver2[0];
281        ver2_ptr = strchr(ch_ver2, ':') + 1;
282    } else {
283        epoch2 = '0';
284        ver2_ptr = ch_ver2;
285    }
286    if (epoch1 < epoch2) {
287        return(-1);
288    }
289    else if (epoch1 > epoch2) {
290        return(1);
291    }
292
293    /* Compare upstream version */
294    upstream_ver1 = bb_xstrdup(ver1_ptr);
295    upstream_ver2 = bb_xstrdup(ver2_ptr);
296
297    /* Chop off debian version, and store for later use */
298    deb_ver1 = strrchr(upstream_ver1, '-');
299    deb_ver2 = strrchr(upstream_ver2, '-');
300    if (deb_ver1) {
301        deb_ver1[0] = '\0';
302        deb_ver1++;
303    }
304    if (deb_ver2) {
305        deb_ver2[0] = '\0';
306        deb_ver2++;
307    }
308    result = version_compare_part(upstream_ver1, upstream_ver2);
309
310    free(upstream_ver1);
311    free(upstream_ver2);
312
313    if (result != 0) {
314        return(result);
315    }
316
317    /* Compare debian versions */
318    return(version_compare_part(deb_ver1, deb_ver2));
319}
320
321static int test_version(const unsigned int version1, const unsigned int version2, const unsigned int operator)
322{
323    const int version_result = version_compare(version1, version2);
324    switch(operator) {
325        case (VER_ANY):
326            return(TRUE);
327        case (VER_EQUAL):
328            if (version_result == 0) {
329                return(TRUE);
330            }
331            break;
332        case (VER_LESS):
333            if (version_result < 0) {
334                return(TRUE);
335            }
336            break;
337        case (VER_LESS_EQUAL):
338            if (version_result <= 0) {
339                return(TRUE);
340            }
341            break;
342        case (VER_MORE):
343            if (version_result > 0) {
344                return(TRUE);
345            }
346            break;
347        case (VER_MORE_EQUAL):
348            if (version_result >= 0) {
349                return(TRUE);
350            }
351            break;
352    }
353    return(FALSE);
354}
355
356
357static int search_package_hashtable(const unsigned int name, const unsigned int version, const unsigned int operator)
358{
359    unsigned int probe_address = 0;
360    unsigned int probe_decrement = 0;
361
362    make_hash(name_hashtable[name], &probe_address, &probe_decrement, PACKAGE_HASH_PRIME);
363    while(package_hashtable[probe_address] != NULL) {
364        if (package_hashtable[probe_address]->name == name) {
365            if (operator == VER_ANY) {
366                return(probe_address);
367            }
368            if (test_version(package_hashtable[probe_address]->version, version, operator)) {
369                return(probe_address);
370            }
371        }
372        probe_address -= probe_decrement;
373        if ((int)probe_address < 0) {
374            probe_address += PACKAGE_HASH_PRIME;
375        }
376    }
377    return(probe_address);
378}
379
380/*
381 * This function searches through the entire package_hashtable looking
382 * for a package which provides "needle". It returns the index into
383 * the package_hashtable for the providing package.
384 *
385 * needle is the index into name_hashtable of the package we are
386 * looking for.
387 *
388 * start_at is the index in the package_hashtable to start looking
389 * at. If start_at is -1 then start at the beginning. This is to allow
390 * for repeated searches since more than one package might provide
391 * needle.
392 *
393 * FIXME: I don't think this is very efficient, but I thought I'd keep
394 * it simple for now until it proves to be a problem.
395 */
396static int search_for_provides(int needle, int start_at) {
397    int i, j;
398    common_node_t *p;
399    for (i = start_at + 1; i < PACKAGE_HASH_PRIME; i++) {
400        p = package_hashtable[i];
401        if ( p == NULL ) continue;
402        for(j = 0; j < p->num_of_edges; j++)
403            if ( p->edge[j]->type == EDGE_PROVIDES && p->edge[j]->name == needle )
404                return i;
405    }
406    return -1;
407}
408
409/*
410 * Add an edge to a node
411 */
412static void add_edge_to_node(common_node_t *node, edge_t *edge)
413{
414    node->num_of_edges++;
415    node->edge = xrealloc(node->edge, sizeof(edge_t) * (node->num_of_edges + 1));
416    node->edge[node->num_of_edges - 1] = edge;
417}
418
419/*
420 * Create one new node and one new edge for every dependency.
421 *
422 * Dependencies which contain multiple alternatives are represented as
423 * an EDGE_OR_PRE_DEPENDS or EDGE_OR_DEPENDS node, followed by a
424 * number of EDGE_PRE_DEPENDS or EDGE_DEPENDS nodes. The name field of
425 * the OR edge contains the full dependency string while the version
426 * field contains the number of EDGE nodes which follow as part of
427 * this alternative.
428 */
429static void add_split_dependencies(common_node_t *parent_node, const char *whole_line, unsigned int edge_type)
430{
431    char *line = bb_xstrdup(whole_line);
432    char *line2;
433    char *line_ptr1 = NULL;
434    char *line_ptr2 = NULL;
435    char *field;
436    char *field2;
437    char *version;
438    edge_t *edge;
439    edge_t *or_edge;
440    int offset_ch;
441
442    field = strtok_r(line, ",", &line_ptr1);
443    do {
444        /* skip leading spaces */
445        field += strspn(field, " ");
446        line2 = bb_xstrdup(field);
447        field2 = strtok_r(line2, "|", &line_ptr2);
448        if ( (edge_type == EDGE_DEPENDS || edge_type == EDGE_PRE_DEPENDS) &&
449             (strcmp(field, field2) != 0)) {
450            or_edge = (edge_t *)xmalloc(sizeof(edge_t));
451            or_edge->type = edge_type + 1;
452        } else {
453            or_edge = NULL;
454        }
455
456        if ( or_edge ) {
457            or_edge->name = search_name_hashtable(field);
458            or_edge->version = 0; // tracks the number of altenatives
459
460            add_edge_to_node(parent_node, or_edge);
461        }
462
463        do {
464            edge = (edge_t *) xmalloc(sizeof(edge_t));
465            edge->type = edge_type;
466
467            /* Skip any extra leading spaces */
468            field2 += strspn(field2, " ");
469
470            /* Get dependency version info */
471            version = strchr(field2, '(');
472            if (version == NULL) {
473                edge->operator = VER_ANY;
474                /* Get the versions hash number, adding it if the number isnt already in there */
475                edge->version = search_name_hashtable("ANY");
476            } else {
477                /* Skip leading ' ' or '(' */
478                version += strspn(field2, " ");
479                version += strspn(version, "(");
480                /* Calculate length of any operator characters */
481                offset_ch = strspn(version, "<=>");
482                /* Determine operator */
483                if (offset_ch > 0) {
484                    if (strncmp(version, "=", offset_ch) == 0) {
485                        edge->operator = VER_EQUAL;
486                    }
487                    else if (strncmp(version, "<<", offset_ch) == 0) {
488                        edge->operator = VER_LESS;
489                    }
490                    else if (strncmp(version, "<=", offset_ch) == 0) {
491                        edge->operator = VER_LESS_EQUAL;
492                    }
493                    else if (strncmp(version, ">>", offset_ch) == 0) {
494                        edge->operator = VER_MORE;
495                    }
496                    else if (strncmp(version, ">=", offset_ch) == 0) {
497                        edge->operator = VER_MORE_EQUAL;
498                    } else {
499                        bb_error_msg_and_die("Illegal operator\n");
500                    }
501                }
502                /* skip to start of version numbers */
503                version += offset_ch;
504                version += strspn(version, " ");
505
506                /* Truncate version at trailing ' ' or ')' */
507                version[strcspn(version, " )")] = '\0';
508                /* Get the versions hash number, adding it if the number isnt already in there */
509                edge->version = search_name_hashtable(version);
510            }
511
512            /* Get the dependency name */
513            field2[strcspn(field2, " (")] = '\0';
514            edge->name = search_name_hashtable(field2);
515
516            if ( or_edge )
517                or_edge->version++;
518
519            add_edge_to_node(parent_node, edge);
520        } while ((field2 = strtok_r(NULL, "|", &line_ptr2)) != NULL);
521        free(line2);
522    } while ((field = strtok_r(NULL, ",", &line_ptr1)) != NULL);
523    free(line);
524
525    return;
526}
527
528static void free_package(common_node_t *node)
529{
530    unsigned short i;
531    if (node) {
532        for (i = 0; i < node->num_of_edges; i++) {
533            free(node->edge[i]);
534        }
535        free(node->edge);
536        free(node);
537    }
538}
539
540static unsigned int fill_package_struct(char *control_buffer)
541{
542    static const char *const field_names[] = { "Package", "Version",
543        "Pre-Depends", "Depends","Replaces", "Provides",
544        "Conflicts", "Suggests", "Recommends", "Enhances", 0
545    };
546
547    common_node_t *new_node = (common_node_t *) xzalloc(sizeof(common_node_t));
548    char *field_name;
549    char *field_value;
550    int field_start = 0;
551    int num = -1;
552    int buffer_length = strlen(control_buffer);
553
554    new_node->version = search_name_hashtable("unknown");
555    while (field_start < buffer_length) {
556        unsigned short field_num;
557
558        field_start += read_package_field(&control_buffer[field_start],
559                &field_name, &field_value);
560
561        if (field_name == NULL) {
562            goto fill_package_struct_cleanup; /* Oh no, the dreaded goto statement ! */
563        }
564
565        field_num = compare_string_array(field_names, field_name);
566        switch(field_num) {
567            case 0: /* Package */
568                new_node->name = search_name_hashtable(field_value);
569                break;
570            case 1: /* Version */
571                new_node->version = search_name_hashtable(field_value);
572                break;
573            case 2: /* Pre-Depends */
574                add_split_dependencies(new_node, field_value, EDGE_PRE_DEPENDS);
575                break;
576            case 3: /* Depends */
577                add_split_dependencies(new_node, field_value, EDGE_DEPENDS);
578                break;
579            case 4: /* Replaces */
580                add_split_dependencies(new_node, field_value, EDGE_REPLACES);
581                break;
582            case 5: /* Provides */
583                add_split_dependencies(new_node, field_value, EDGE_PROVIDES);
584                break;
585            case 6: /* Conflicts */
586                add_split_dependencies(new_node, field_value, EDGE_CONFLICTS);
587                break;
588            case 7: /* Suggests */
589                add_split_dependencies(new_node, field_value, EDGE_SUGGESTS);
590                break;
591            case 8: /* Recommends */
592                add_split_dependencies(new_node, field_value, EDGE_RECOMMENDS);
593                break;
594            case 9: /* Enhances */
595                add_split_dependencies(new_node, field_value, EDGE_ENHANCES);
596                break;
597        }
598fill_package_struct_cleanup:
599        free(field_name);
600        free(field_value);
601    }
602
603    if (new_node->version == search_name_hashtable("unknown")) {
604        free_package(new_node);
605        return(-1);
606    }
607    num = search_package_hashtable(new_node->name, new_node->version, VER_EQUAL);
608    if (package_hashtable[num] == NULL) {
609        package_hashtable[num] = new_node;
610    } else {
611        free_package(new_node);
612    }
613    return(num);
614}
615
616/* if num = 1, it returns the want status, 2 returns flag, 3 returns status */
617static unsigned int get_status(const unsigned int status_node, const int num)
618{
619    char *status_string = name_hashtable[status_hashtable[status_node]->status];
620    char *state_sub_string;
621    unsigned int state_sub_num;
622    int len;
623    int i;
624
625    /* set tmp_string to point to the start of the word number */
626    for (i = 1; i < num; i++) {
627        /* skip past a word */
628        status_string += strcspn(status_string, " ");
629        /* skip past the separating spaces */
630        status_string += strspn(status_string, " ");
631    }
632    len = strcspn(status_string, " \n\0");
633    state_sub_string = bb_xstrndup(status_string, len);
634    state_sub_num = search_name_hashtable(state_sub_string);
635    free(state_sub_string);
636    return(state_sub_num);
637}
638
639static void set_status(const unsigned int status_node_num, const char *new_value, const int position)
640{
641    const unsigned int new_value_len = strlen(new_value);
642    const unsigned int new_value_num = search_name_hashtable(new_value);
643    unsigned int want = get_status(status_node_num, 1);
644    unsigned int flag = get_status(status_node_num, 2);
645    unsigned int status = get_status(status_node_num, 3);
646    int want_len = strlen(name_hashtable[want]);
647    int flag_len = strlen(name_hashtable[flag]);
648    int status_len = strlen(name_hashtable[status]);
649    char *new_status;
650
651    switch (position) {
652        case (1):
653            want = new_value_num;
654            want_len = new_value_len;
655            break;
656        case (2):
657            flag = new_value_num;
658            flag_len = new_value_len;
659            break;
660        case (3):
661            status = new_value_num;
662            status_len = new_value_len;
663            break;
664        default:
665            bb_error_msg_and_die("DEBUG ONLY: this shouldnt happen");
666    }
667
668    new_status = bb_xasprintf("%s %s %s", name_hashtable[want], name_hashtable[flag], name_hashtable[status]);
669    status_hashtable[status_node_num]->status = search_name_hashtable(new_status);
670    free(new_status);
671    return;
672}
673
674static const char *describe_status(int status_num) {
675    int status_want, status_state ;
676    if ( status_hashtable[status_num] == NULL || status_hashtable[status_num]->status == 0 )
677        return "is not installed or flagged to be installed\n";
678
679    status_want = get_status(status_num, 1);
680    status_state = get_status(status_num, 3);
681
682    if ( status_state == search_name_hashtable("installed") ) {
683        if ( status_want == search_name_hashtable("install") )
684            return "is installed";
685        if ( status_want == search_name_hashtable("deinstall") )
686            return "is marked to be removed";
687        if ( status_want == search_name_hashtable("purge") )
688            return "is marked to be purged";
689    }
690    if ( status_want ==  search_name_hashtable("unknown") )
691        return "is in an indeterminate state";
692    if ( status_want == search_name_hashtable("install") )
693        return "is marked to be installed";
694
695    return "is not installed or flagged to be installed";
696}
697
698
699static void index_status_file(const char *filename)
700{
701    FILE *status_file;
702    char *control_buffer;
703    char *status_line;
704    status_node_t *status_node = NULL;
705    unsigned int status_num;
706
707    status_file = bb_xfopen(filename, "r");
708    while ((control_buffer = fgets_str(status_file, "\n\n")) != NULL) {
709        const unsigned int package_num = fill_package_struct(control_buffer);
710        if (package_num != -1) {
711            status_node = xmalloc(sizeof(status_node_t));
712            /* fill_package_struct doesnt handle the status field */
713            status_line = strstr(control_buffer, "Status:");
714            if (status_line != NULL) {
715                status_line += 7;
716                status_line += strspn(status_line, " \n\t");
717                status_line = bb_xstrndup(status_line, strcspn(status_line, "\n\0"));
718                status_node->status = search_name_hashtable(status_line);
719                free(status_line);
720            }
721            status_node->package = package_num;
722            status_num = search_status_hashtable(name_hashtable[package_hashtable[status_node->package]->name]);
723            status_hashtable[status_num] = status_node;
724        }
725        free(control_buffer);
726    }
727    fclose(status_file);
728    return;
729}
730
731static void write_buffer_no_status(FILE *new_status_file, const char *control_buffer)
732{
733    char *name;
734    char *value;
735    int start = 0;
736    while (1) {
737        start += read_package_field(&control_buffer[start], &name, &value);
738        if (name == NULL) {
739            break;
740        }
741        if (strcmp(name, "Status") != 0) {
742            fprintf(new_status_file, "%s: %s\n", name, value);
743        }
744    }
745    return;
746}
747
748/* This could do with a cleanup */
749static void write_status_file(deb_file_t **deb_file)
750{
751    FILE *old_status_file = bb_xfopen("/var/lib/dpkg/status", "r");
752    FILE *new_status_file = bb_xfopen("/var/lib/dpkg/status.udeb", "w");
753    char *package_name;
754    char *status_from_file;
755    char *control_buffer = NULL;
756    char *tmp_string;
757    int status_num;
758    int field_start = 0;
759    int write_flag;
760    int i = 0;
761
762    /* Update previously known packages */
763    while ((control_buffer = fgets_str(old_status_file, "\n\n")) != NULL) {
764        if ((tmp_string = strstr(control_buffer, "Package:")) == NULL) {
765            continue;
766        }
767
768        tmp_string += 8;
769        tmp_string += strspn(tmp_string, " \n\t");
770        package_name = bb_xstrndup(tmp_string, strcspn(tmp_string, "\n\0"));
771        write_flag = FALSE;
772        tmp_string = strstr(control_buffer, "Status:");
773        if (tmp_string != NULL) {
774            /* Seperate the status value from the control buffer */
775            tmp_string += 7;
776            tmp_string += strspn(tmp_string, " \n\t");
777            status_from_file = bb_xstrndup(tmp_string, strcspn(tmp_string, "\n"));
778        } else {
779            status_from_file = NULL;
780        }
781
782        /* Find this package in the status hashtable */
783        status_num = search_status_hashtable(package_name);
784        if (status_hashtable[status_num] != NULL) {
785            const char *status_from_hashtable = name_hashtable[status_hashtable[status_num]->status];
786            if (strcmp(status_from_file, status_from_hashtable) != 0) {
787                /* New status isnt exactly the same as old status */
788                const int state_status = get_status(status_num, 3);
789                if ((strcmp("installed", name_hashtable[state_status]) == 0) ||
790                    (strcmp("unpacked", name_hashtable[state_status]) == 0)) {
791                    /* We need to add the control file from the package */
792                    i = 0;
793                    while(deb_file[i] != NULL) {
794                        if (strcmp(package_name, name_hashtable[package_hashtable[deb_file[i]->package]->name]) == 0) {
795                            /* Write a status file entry with a modified status */
796                            /* remove trailing \n's */
797                            write_buffer_no_status(new_status_file, deb_file[i]->control_file);
798                            set_status(status_num, "ok", 2);
799                            fprintf(new_status_file, "Status: %s\n\n", name_hashtable[status_hashtable[status_num]->status]);
800                            write_flag = TRUE;
801                            break;
802                        }
803                        i++;
804                    }
805                    /* This is temperary, debugging only */
806                    if (deb_file[i] == NULL) {
807                        bb_error_msg_and_die("ALERT: Couldnt find a control file, your status file may be broken, status may be incorrect for %s", package_name);
808                    }
809                }
810                else if (strcmp("not-installed", name_hashtable[state_status]) == 0) {
811                    /* Only write the Package, Status, Priority and Section lines */
812                    fprintf(new_status_file, "Package: %s\n", package_name);
813                    fprintf(new_status_file, "Status: %s\n", status_from_hashtable);
814
815                    while (1) {
816                        char *field_name;
817                        char *field_value;
818                        field_start += read_package_field(&control_buffer[field_start], &field_name, &field_value);
819                        if (field_name == NULL) {
820                            break;
821                        }
822                        if ((strcmp(field_name, "Priority") == 0) ||
823                            (strcmp(field_name, "Section") == 0)) {
824                            fprintf(new_status_file, "%s: %s\n", field_name, field_value);
825                        }
826                    }
827                    write_flag = TRUE;
828                    fputs("\n", new_status_file);
829                }
830                else if (strcmp("config-files", name_hashtable[state_status]) == 0) {
831                    /* only change the status line */
832                    while (1) {
833                        char *field_name;
834                        char *field_value;
835                        field_start += read_package_field(&control_buffer[field_start], &field_name, &field_value);
836                        if (field_name == NULL) {
837                            break;
838                        }
839                        /* Setup start point for next field */
840                        if (strcmp(field_name, "Status") == 0) {
841                            fprintf(new_status_file, "Status: %s\n", status_from_hashtable);
842                        } else {
843                            fprintf(new_status_file, "%s: %s\n", field_name, field_value);
844                        }
845                    }
846                    write_flag = TRUE;
847                    fputs("\n", new_status_file);
848                }
849            }
850        }
851        /* If the package from the status file wasnt handle above, do it now*/
852        if (! write_flag) {
853            fprintf(new_status_file, "%s\n\n", control_buffer);
854        }
855
856        free(status_from_file);
857        free(package_name);
858        free(control_buffer);
859    }
860
861    /* Write any new packages */
862    for(i = 0; deb_file[i] != NULL; i++) {
863        status_num = search_status_hashtable(name_hashtable[package_hashtable[deb_file[i]->package]->name]);
864        if (strcmp("reinstreq", name_hashtable[get_status(status_num, 2)]) == 0) {
865            write_buffer_no_status(new_status_file, deb_file[i]->control_file);
866            set_status(status_num, "ok", 2);
867            fprintf(new_status_file, "Status: %s\n\n", name_hashtable[status_hashtable[status_num]->status]);
868        }
869    }
870    fclose(old_status_file);
871    fclose(new_status_file);
872
873
874    /* Create a separate backfile to dpkg */
875    if (rename("/var/lib/dpkg/status", "/var/lib/dpkg/status.udeb.bak") == -1) {
876        struct stat stat_buf;
877        xstat("/var/lib/dpkg/status", &stat_buf);
878        /* Its ok if renaming the status file fails because status
879         * file doesnt exist, maybe we are starting from scratch */
880        bb_error_msg("No status file found, creating new one");
881    }
882
883    if (rename("/var/lib/dpkg/status.udeb", "/var/lib/dpkg/status") == -1) {
884        bb_error_msg_and_die("DANGER: Couldnt create status file, you need to manually repair your status file");
885    }
886}
887
888/* This function returns TRUE if the given package can satisfy a
889 * dependency of type depend_type.
890 *
891 * A pre-depends is satisfied only if a package is already installed,
892 * which a regular depends can be satisfied by a package which we want
893 * to install.
894 */
895static int package_satisfies_dependency(int package, int depend_type)
896{
897    int status_num = search_status_hashtable(name_hashtable[package_hashtable[package]->name]);
898
899    /* status could be unknown if package is a pure virtual
900     * provides which cannot satisfy any dependency by itself.
901     */
902    if ( status_hashtable[status_num] == NULL )
903        return 0;
904
905    switch (depend_type) {
906    case EDGE_PRE_DEPENDS:  return get_status(status_num, 3) == search_name_hashtable("installed");
907    case EDGE_DEPENDS:  return get_status(status_num, 1) == search_name_hashtable("install");
908    }
909    return 0;
910}
911
912static int check_deps(deb_file_t **deb_file, int deb_start, int dep_max_count)
913{
914    int *conflicts = NULL;
915    int conflicts_num = 0;
916    int i = deb_start;
917    int j;
918
919    /* Check for conflicts
920     * TODO: TEST if conflicts with other packages to be installed
921     *
922     * Add install packages and the packages they provide
923     * to the list of files to check conflicts for
924     */
925
926    /* Create array of package numbers to check against
927     * installed package for conflicts*/
928    while (deb_file[i] != NULL) {
929        const unsigned int package_num = deb_file[i]->package;
930        conflicts = xrealloc(conflicts, sizeof(int) * (conflicts_num + 1));
931        conflicts[conflicts_num] = package_num;
932        conflicts_num++;
933        /* add provides to conflicts list */
934        for (j = 0; j < package_hashtable[package_num]->num_of_edges; j++) {
935            if (package_hashtable[package_num]->edge[j]->type == EDGE_PROVIDES) {
936                const int conflicts_package_num = search_package_hashtable(
937                    package_hashtable[package_num]->edge[j]->name,
938                    package_hashtable[package_num]->edge[j]->version,
939                    package_hashtable[package_num]->edge[j]->operator);
940                if (package_hashtable[conflicts_package_num] == NULL) {
941                    /* create a new package */
942                    common_node_t *new_node = (common_node_t *) xzalloc(sizeof(common_node_t));
943                    new_node->name = package_hashtable[package_num]->edge[j]->name;
944                    new_node->version = package_hashtable[package_num]->edge[j]->version;
945                    package_hashtable[conflicts_package_num] = new_node;
946                }
947                conflicts = xrealloc(conflicts, sizeof(int) * (conflicts_num + 1));
948                conflicts[conflicts_num] = conflicts_package_num;
949                conflicts_num++;
950            }
951        }
952        i++;
953    }
954
955    /* Check conflicts */
956    i = 0;
957    while (deb_file[i] != NULL) {
958        const common_node_t *package_node = package_hashtable[deb_file[i]->package];
959        int status_num = 0;
960        status_num = search_status_hashtable(name_hashtable[package_node->name]);
961
962        if (get_status(status_num, 3) == search_name_hashtable("installed")) {
963            i++;
964            continue;
965        }
966
967        for (j = 0; j < package_node->num_of_edges; j++) {
968            const edge_t *package_edge = package_node->edge[j];
969
970            if (package_edge->type == EDGE_CONFLICTS) {
971                const unsigned int package_num =
972                    search_package_hashtable(package_edge->name,
973                                 package_edge->version,
974                                 package_edge->operator);
975                int result = 0;
976                if (package_hashtable[package_num] != NULL) {
977                    status_num = search_status_hashtable(name_hashtable[package_hashtable[package_num]->name]);
978
979                    if (get_status(status_num, 1) == search_name_hashtable("install")) {
980                        result = test_version(package_hashtable[deb_file[i]->package]->version,
981                            package_edge->version, package_edge->operator);
982                    }
983                }
984
985                if (result) {
986                    bb_error_msg_and_die("Package %s conflicts with %s",
987                        name_hashtable[package_node->name],
988                        name_hashtable[package_edge->name]);
989                }
990            }
991        }
992        i++;
993    }
994
995
996    /* Check dependendcies */
997    for (i = 0; i < PACKAGE_HASH_PRIME; i++) {
998        int status_num = 0;
999        int number_of_alternatives = 0;
1000        const edge_t * root_of_alternatives = NULL;
1001        const common_node_t *package_node = package_hashtable[i];
1002
1003        /* If the package node does not exist then this
1004         * package is a virtual one. In which case there are
1005         * no dependencies to check.
1006         */
1007        if ( package_node == NULL ) continue;
1008
1009        status_num = search_status_hashtable(name_hashtable[package_node->name]);
1010
1011        /* If there is no status then this package is a
1012         * virtual one provided by something else. In which
1013         * case there are no dependencies to check.
1014         */
1015        if ( status_hashtable[status_num] == NULL ) continue;
1016
1017        /* If we don't want this package installed then we may
1018         * as well ignore it's dependencies.
1019         */
1020        if (get_status(status_num, 1) != search_name_hashtable("install")) {
1021            continue;
1022        }
1023
1024#if 0
1025        /* This might be needed so we don't complain about
1026         * things which are broken but unrelated to the
1027         * packages that are currently being installed
1028         */
1029        if (state_status == search_name_hashtable("installed"))
1030            continue;
1031#endif
1032
1033        /* This code is tested only for EDGE_DEPENDS, since I
1034         * have no suitable pre-depends available. There is no
1035         * reason that it shouldn't work though :-)
1036         */
1037        for (j = 0; j < package_node->num_of_edges; j++) {
1038            const edge_t *package_edge = package_node->edge[j];
1039            unsigned int package_num;
1040
1041            if ( package_edge->type == EDGE_OR_PRE_DEPENDS ||
1042                 package_edge->type == EDGE_OR_DEPENDS ) {  /* start an EDGE_OR_ list */
1043                number_of_alternatives = package_edge->version;
1044                root_of_alternatives = package_edge;
1045                continue;
1046            } else if ( number_of_alternatives == 0 ) { /* not in the middle of an EDGE_OR_ list */
1047                number_of_alternatives = 1;
1048                root_of_alternatives = NULL;
1049            }
1050
1051            package_num = search_package_hashtable(package_edge->name, package_edge->version, package_edge->operator);
1052
1053            if (package_edge->type == EDGE_PRE_DEPENDS ||
1054                package_edge->type == EDGE_DEPENDS ) {
1055                int result=1;
1056                status_num = 0;
1057
1058                /* If we are inside an alternative then check
1059                 * this edge is the right type.
1060                 *
1061                 * EDGE_DEPENDS == OR_DEPENDS -1
1062                 * EDGE_PRE_DEPENDS == OR_PRE_DEPENDS -1
1063                 */
1064                if ( root_of_alternatives && package_edge->type != root_of_alternatives->type - 1)
1065                    bb_error_msg_and_die("Fatal error. Package dependencies corrupt: %d != %d - 1 \n",
1066                                 package_edge->type, root_of_alternatives->type);
1067
1068                if (package_hashtable[package_num] != NULL)
1069                    result = !package_satisfies_dependency(package_num, package_edge->type);
1070
1071                if (result) { /* check for other package which provide what we are looking for */
1072                    int provider = -1;
1073
1074                    while ( (provider = search_for_provides(package_edge->name, provider) ) > -1 ) {
1075                        if ( package_hashtable[provider] == NULL ) {
1076                            printf("Have a provider but no package information for it\n");
1077                            continue;
1078                        }
1079                        result = !package_satisfies_dependency(provider, package_edge->type);
1080
1081                        if ( result == 0 )
1082                            break;
1083                    }
1084                }
1085
1086                /* It must be already installed, or to be installed */
1087                number_of_alternatives--;
1088                if (result && number_of_alternatives == 0) {
1089                    if ( root_of_alternatives )
1090                        bb_error_msg_and_die(
1091                            "Package %s %sdepends on %s, "
1092                            "which cannot be satisfied",
1093                            name_hashtable[package_node->name],
1094                            package_edge->type == EDGE_PRE_DEPENDS ? "pre-" : "",
1095                            name_hashtable[root_of_alternatives->name]);
1096                    else
1097                        bb_error_msg_and_die(
1098                            "Package %s %sdepends on %s, which %s\n",
1099                            name_hashtable[package_node->name],
1100                            package_edge->type == EDGE_PRE_DEPENDS ? "pre-" : "",
1101                            name_hashtable[package_edge->name],
1102                            describe_status(status_num));
1103                } else if ( result == 0 && number_of_alternatives ) {
1104                    /* we've found a package which
1105                     * satisfies the dependency,
1106                     * so skip over the rest of
1107                     * the alternatives.
1108                     */
1109                    j += number_of_alternatives;
1110                    number_of_alternatives = 0;
1111                }
1112            }
1113        }
1114    }
1115    free(conflicts);
1116    return(TRUE);
1117}
1118
1119static char **create_list(const char *filename)
1120{
1121    FILE *list_stream;
1122    char **file_list = NULL;
1123    char *line = NULL;
1124    int count = 0;
1125
1126    /* don't use [xw]fopen here, handle error ourself */
1127    list_stream = fopen(filename, "r");
1128    if (list_stream == NULL) {
1129        return(NULL);
1130    }
1131
1132    while ((line = bb_get_chomped_line_from_file(list_stream)) != NULL) {
1133        file_list = xrealloc(file_list, sizeof(char *) * (count + 2));
1134        file_list[count] = line;
1135        count++;
1136    }
1137    fclose(list_stream);
1138
1139    if (count == 0) {
1140        return(NULL);
1141    } else {
1142        file_list[count] = NULL;
1143        return(file_list);
1144    }
1145}
1146
1147/* maybe i should try and hook this into remove_file.c somehow */
1148static int remove_file_array(char **remove_names, char **exclude_names)
1149{
1150    struct stat path_stat;
1151    int match_flag;
1152    int remove_flag = FALSE;
1153    int i,j;
1154
1155    if (remove_names == NULL) {
1156        return(FALSE);
1157    }
1158    for (i = 0; remove_names[i] != NULL; i++) {
1159        match_flag = FALSE;
1160        if (exclude_names != NULL) {
1161            for (j = 0; exclude_names[j] != 0; j++) {
1162                if (strcmp(remove_names[i], exclude_names[j]) == 0) {
1163                    match_flag = TRUE;
1164                    break;
1165                }
1166            }
1167        }
1168        if (!match_flag) {
1169            if (lstat(remove_names[i], &path_stat) < 0) {
1170                continue;
1171            }
1172            if (S_ISDIR(path_stat.st_mode)) {
1173                if (rmdir(remove_names[i]) != -1) {
1174                    remove_flag = TRUE;
1175                }
1176            } else {
1177                if (unlink(remove_names[i]) != -1) {
1178                    remove_flag = TRUE;
1179                }
1180            }
1181        }
1182    }
1183    return(remove_flag);
1184}
1185
1186static int run_package_script(const char *package_name, const char *script_type)
1187{
1188    struct stat path_stat;
1189    char *script_path;
1190    int result;
1191
1192    script_path = bb_xasprintf("/var/lib/dpkg/info/%s.%s", package_name, script_type);
1193
1194    /* If the file doesnt exist is isnt a fatal */
1195    result = lstat(script_path, &path_stat) < 0 ? EXIT_SUCCESS : system(script_path);
1196    free(script_path);
1197    return(result);
1198}
1199
1200static const char *all_control_files[] = {"preinst", "postinst", "prerm", "postrm",
1201    "list", "md5sums", "shlibs", "conffiles", "config", "templates", NULL };
1202
1203static char **all_control_list(const char *package_name)
1204{
1205    unsigned short i = 0;
1206    char **remove_files;
1207
1208    /* Create a list of all /var/lib/dpkg/info/<package> files */
1209    remove_files = xzalloc(sizeof(all_control_files));
1210    while (all_control_files[i]) {
1211        remove_files[i] = bb_xasprintf("/var/lib/dpkg/info/%s.%s", package_name, all_control_files[i]);
1212        i++;
1213    }
1214
1215    return(remove_files);
1216}
1217
1218static void free_array(char **array)
1219{
1220
1221    if (array) {
1222        unsigned short i = 0;
1223        while (array[i]) {
1224            free(array[i]);
1225            i++;
1226        }
1227        free(array);
1228    }
1229}
1230
1231/* This function lists information on the installed packages. It loops through
1232 * the status_hashtable to retrieve the info. This results in smaller code than
1233 * scanning the status file. The resulting list, however, is unsorted.
1234 */
1235static void list_packages(void)
1236{
1237    int i;
1238
1239    printf("    Name           Version\n");
1240    printf("+++-==============-==============\n");
1241
1242    /* go through status hash, dereference package hash and finally strings */
1243    for (i=0; i<STATUS_HASH_PRIME+1; i++) {
1244
1245            if (status_hashtable[i]) {
1246                const char *stat_str;  /* status string */
1247            const char *name_str;  /* package name */
1248            const char *vers_str;  /* version */
1249            char  s1, s2;          /* status abbreviations */
1250            int   spccnt;          /* space count */
1251            int   j;
1252
1253            stat_str = name_hashtable[status_hashtable[i]->status];
1254            name_str = name_hashtable[package_hashtable[status_hashtable[i]->package]->name];
1255            vers_str = name_hashtable[package_hashtable[status_hashtable[i]->package]->version];
1256
1257            /* get abbreviation for status field 1 */
1258            s1 = stat_str[0] == 'i' ? 'i' : 'r';
1259
1260            /* get abbreviation for status field 2 */
1261            for (j=0, spccnt=0; stat_str[j] && spccnt<2; j++) {
1262                    if (stat_str[j] == ' ') spccnt++;
1263            }
1264            s2 = stat_str[j];
1265
1266            /* print out the line formatted like Debian dpkg */
1267            printf("%c%c  %-14s %s\n", s1, s2, name_str, vers_str);
1268        }
1269    }
1270}
1271
1272static void remove_package(const unsigned int package_num, int noisy)
1273{
1274    const char *package_name = name_hashtable[package_hashtable[package_num]->name];
1275    const char *package_version = name_hashtable[package_hashtable[package_num]->version];
1276    const unsigned int status_num = search_status_hashtable(package_name);
1277    const int package_name_length = strlen(package_name);
1278    char **remove_files;
1279    char **exclude_files;
1280    char list_name[package_name_length + 25];
1281    char conffile_name[package_name_length + 30];
1282    int return_value;
1283
1284    if ( noisy )
1285        printf("Removing %s (%s) ...\n", package_name, package_version);
1286
1287    /* run prerm script */
1288    return_value = run_package_script(package_name, "prerm");
1289    if (return_value == -1) {
1290        bb_error_msg_and_die("script failed, prerm failure");
1291    }
1292
1293    /* Create a list of files to remove, and a separate list of those to keep */
1294    sprintf(list_name, "/var/lib/dpkg/info/%s.list", package_name);
1295    remove_files = create_list(list_name);
1296
1297    sprintf(conffile_name, "/var/lib/dpkg/info/%s.conffiles", package_name);
1298    exclude_files = create_list(conffile_name);
1299
1300    /* Some directories cant be removed straight away, so do multiple passes */
1301    while (remove_file_array(remove_files, exclude_files));
1302    free_array(exclude_files);
1303    free_array(remove_files);
1304
1305    /* Create a list of files in /var/lib/dpkg/info/<package>.* to keep  */
1306    exclude_files = xzalloc(sizeof(char*) * 3);
1307    exclude_files[0] = bb_xstrdup(conffile_name);
1308    exclude_files[1] = bb_xasprintf("/var/lib/dpkg/info/%s.postrm", package_name);
1309
1310    /* Create a list of all /var/lib/dpkg/info/<package> files */
1311    remove_files = all_control_list(package_name);
1312
1313    remove_file_array(remove_files, exclude_files);
1314    free_array(remove_files);
1315    free_array(exclude_files);
1316
1317    /* rename <package>.conffile to <package>.list */
1318    rename(conffile_name, list_name);
1319
1320    /* Change package status */
1321    set_status(status_num, "config-files", 3);
1322}
1323
1324static void purge_package(const unsigned int package_num)
1325{
1326    const char *package_name = name_hashtable[package_hashtable[package_num]->name];
1327    const char *package_version = name_hashtable[package_hashtable[package_num]->version];
1328    const unsigned int status_num = search_status_hashtable(package_name);
1329    char **remove_files;
1330    char **exclude_files;
1331    char list_name[strlen(package_name) + 25];
1332
1333    printf("Purging %s (%s) ...\n", package_name, package_version);
1334
1335    /* run prerm script */
1336    if (run_package_script(package_name, "prerm") != 0) {
1337        bb_error_msg_and_die("script failed, prerm failure");
1338    }
1339
1340    /* Create a list of files to remove */
1341    sprintf(list_name, "/var/lib/dpkg/info/%s.list", package_name);
1342    remove_files = create_list(list_name);
1343
1344    exclude_files = xzalloc(sizeof(char*));
1345
1346    /* Some directories cant be removed straight away, so do multiple passes */
1347    while (remove_file_array(remove_files, exclude_files));
1348    free_array(remove_files);
1349
1350    /* Create a list of all /var/lib/dpkg/info/<package> files */
1351    remove_files = all_control_list(package_name);
1352    remove_file_array(remove_files, exclude_files);
1353    free_array(remove_files);
1354    free(exclude_files);
1355
1356    /* run postrm script */
1357    if (run_package_script(package_name, "postrm") == -1) {
1358        bb_error_msg_and_die("postrm fialure.. set status to what?");
1359    }
1360
1361    /* Change package status */
1362    set_status(status_num, "not-installed", 3);
1363}
1364
1365static archive_handle_t *init_archive_deb_ar(const char *filename)
1366{
1367    archive_handle_t *ar_handle;
1368
1369    /* Setup an ar archive handle that refers to the gzip sub archive */
1370    ar_handle = init_handle();
1371    ar_handle->filter = filter_accept_list_reassign;
1372    ar_handle->src_fd = bb_xopen(filename, O_RDONLY);
1373
1374    return(ar_handle);
1375}
1376
1377static void init_archive_deb_control(archive_handle_t *ar_handle)
1378{
1379    archive_handle_t *tar_handle;
1380
1381    /* Setup the tar archive handle */
1382    tar_handle = init_handle();
1383    tar_handle->src_fd = ar_handle->src_fd;
1384
1385    /* We don't care about data.tar.* or debian-binary, just control.tar.* */
1386#ifdef CONFIG_FEATURE_DEB_TAR_GZ
1387    llist_add_to(&(ar_handle->accept), "control.tar.gz");
1388#endif
1389#ifdef CONFIG_FEATURE_DEB_TAR_BZ2
1390    llist_add_to(&(ar_handle->accept), "control.tar.bz2");
1391#endif
1392
1393    /* Assign the tar handle as a subarchive of the ar handle */
1394    ar_handle->sub_archive = tar_handle;
1395
1396    return;
1397}
1398
1399static void init_archive_deb_data(archive_handle_t *ar_handle)
1400{
1401    archive_handle_t *tar_handle;
1402
1403    /* Setup the tar archive handle */
1404    tar_handle = init_handle();
1405    tar_handle->src_fd = ar_handle->src_fd;
1406
1407    /* We don't care about control.tar.* or debian-binary, just data.tar.* */
1408#ifdef CONFIG_FEATURE_DEB_TAR_GZ
1409    llist_add_to(&(ar_handle->accept), "data.tar.gz");
1410#endif
1411#ifdef CONFIG_FEATURE_DEB_TAR_BZ2
1412    llist_add_to(&(ar_handle->accept), "data.tar.bz2");
1413#endif
1414
1415    /* Assign the tar handle as a subarchive of the ar handle */
1416    ar_handle->sub_archive = tar_handle;
1417
1418    return;
1419}
1420
1421static char *deb_extract_control_file_to_buffer(archive_handle_t *ar_handle, llist_t *myaccept)
1422{
1423    ar_handle->sub_archive->action_data = data_extract_to_buffer;
1424    ar_handle->sub_archive->accept = myaccept;
1425    ar_handle->sub_archive->filter = filter_accept_list;
1426
1427    unpack_ar_archive(ar_handle);
1428    close(ar_handle->src_fd);
1429
1430    return(ar_handle->sub_archive->buffer);
1431}
1432
1433static void data_extract_all_prefix(archive_handle_t *archive_handle)
1434{
1435    char *name_ptr = archive_handle->file_header->name;
1436
1437    name_ptr += strspn(name_ptr, "./");
1438    if (name_ptr[0] != '\0') {
1439        archive_handle->file_header->name = bb_xasprintf("%s%s", archive_handle->buffer, name_ptr);
1440        data_extract_all(archive_handle);
1441    }
1442    return;
1443}
1444
1445static void unpack_package(deb_file_t *deb_file)
1446{
1447    const char *package_name = name_hashtable[package_hashtable[deb_file->package]->name];
1448    const unsigned int status_num = search_status_hashtable(package_name);
1449    const unsigned int status_package_num = status_hashtable[status_num]->package;
1450    char *info_prefix;
1451    archive_handle_t *archive_handle;
1452    FILE *out_stream;
1453    llist_t *accept_list = NULL;
1454    int i = 0;
1455
1456    /* If existing version, remove it first */
1457    if (strcmp(name_hashtable[get_status(status_num, 3)], "installed") == 0) {
1458        /* Package is already installed, remove old version first */
1459        printf("Preparing to replace %s %s (using %s) ...\n", package_name,
1460            name_hashtable[package_hashtable[status_package_num]->version],
1461            deb_file->filename);
1462        remove_package(status_package_num, 0);
1463    } else {
1464        printf("Unpacking %s (from %s) ...\n", package_name, deb_file->filename);
1465    }
1466
1467    /* Extract control.tar.gz to /var/lib/dpkg/info/<package>.filename */
1468    info_prefix = bb_xasprintf("/var/lib/dpkg/info/%s.", package_name);
1469    archive_handle = init_archive_deb_ar(deb_file->filename);
1470    init_archive_deb_control(archive_handle);
1471
1472    while(all_control_files[i]) {
1473        char *c = bb_xasprintf("./%s", all_control_files[i]);
1474        llist_add_to(&accept_list, c);
1475        i++;
1476    }
1477    archive_handle->sub_archive->accept = accept_list;
1478    archive_handle->sub_archive->filter = filter_accept_list;
1479    archive_handle->sub_archive->action_data = data_extract_all_prefix;
1480    archive_handle->sub_archive->buffer = info_prefix;
1481    archive_handle->sub_archive->flags |= ARCHIVE_EXTRACT_UNCONDITIONAL;
1482    unpack_ar_archive(archive_handle);
1483
1484    /* Run the preinst prior to extracting */
1485    if (run_package_script(package_name, "preinst") != 0) {
1486        /* when preinst returns exit code != 0 then quit installation process */
1487        bb_error_msg_and_die("subprocess pre-installation script returned error.");
1488    }
1489
1490    /* Extract data.tar.gz to the root directory */
1491    archive_handle = init_archive_deb_ar(deb_file->filename);
1492    init_archive_deb_data(archive_handle);
1493    archive_handle->sub_archive->action_data = data_extract_all_prefix;
1494    archive_handle->sub_archive->buffer = "/";
1495    archive_handle->sub_archive->flags |= ARCHIVE_EXTRACT_UNCONDITIONAL;
1496    unpack_ar_archive(archive_handle);
1497
1498    /* Create the list file */
1499    strcat(info_prefix, "list");
1500    out_stream = bb_xfopen(info_prefix, "w");
1501    while (archive_handle->sub_archive->passed) {
1502        /* the leading . has been stripped by data_extract_all_prefix already */
1503        fputs(archive_handle->sub_archive->passed->data, out_stream);
1504        fputc('\n', out_stream);
1505        archive_handle->sub_archive->passed = archive_handle->sub_archive->passed->link;
1506    }
1507    fclose(out_stream);
1508
1509    /* change status */
1510    set_status(status_num, "install", 1);
1511    set_status(status_num, "unpacked", 3);
1512
1513    free(info_prefix);
1514}
1515
1516static void configure_package(deb_file_t *deb_file)
1517{
1518    const char *package_name = name_hashtable[package_hashtable[deb_file->package]->name];
1519    const char *package_version = name_hashtable[package_hashtable[deb_file->package]->version];
1520    const int status_num = search_status_hashtable(package_name);
1521
1522    printf("Setting up %s (%s) ...\n", package_name, package_version);
1523
1524    /* Run the postinst script */
1525    if (run_package_script(package_name, "postinst") != 0) {
1526        /* TODO: handle failure gracefully */
1527        bb_error_msg_and_die("postrm failure.. set status to what?");
1528    }
1529    /* Change status to reflect success */
1530    set_status(status_num, "install", 1);
1531    set_status(status_num, "installed", 3);
1532}
1533
1534int dpkg_main(int argc, char **argv)
1535{
1536    deb_file_t **deb_file = NULL;
1537    status_node_t *status_node;
1538    int opt;
1539    int package_num;
1540    int dpkg_opt = 0;
1541    int deb_count = 0;
1542    int state_status;
1543    int status_num;
1544    int i;
1545
1546    while ((opt = getopt(argc, argv, "CF:ilPru")) != -1) {
1547        switch (opt) {
1548            case 'C': // equivalent to --configure in official dpkg
1549                dpkg_opt |= dpkg_opt_configure;
1550                dpkg_opt |= dpkg_opt_package_name;
1551                break;
1552            case 'F': // equivalent to --force in official dpkg
1553                if (strcmp(optarg, "depends") == 0) {
1554                    dpkg_opt |= dpkg_opt_force_ignore_depends;
1555                }
1556                break;
1557            case 'i':
1558                dpkg_opt |= dpkg_opt_install;
1559                dpkg_opt |= dpkg_opt_filename;
1560                break;
1561            case 'l':
1562                dpkg_opt |= dpkg_opt_list_installed;
1563                break;
1564            case 'P':
1565                dpkg_opt |= dpkg_opt_purge;
1566                dpkg_opt |= dpkg_opt_package_name;
1567                break;
1568            case 'r':
1569                dpkg_opt |= dpkg_opt_remove;
1570                dpkg_opt |= dpkg_opt_package_name;
1571                break;
1572            case 'u':   /* Equivalent to --unpack in official dpkg */
1573                dpkg_opt |= dpkg_opt_unpack;
1574                dpkg_opt |= dpkg_opt_filename;
1575                break;
1576            default:
1577                bb_show_usage();
1578        }
1579    }
1580    /* check for non-otion argument if expected  */
1581    if ((dpkg_opt == 0) || ((argc == optind) && !(dpkg_opt && dpkg_opt_list_installed))) {
1582        bb_show_usage();
1583    }
1584
1585/*  puts("(Reading database ... xxxxx files and directories installed.)"); */
1586    index_status_file("/var/lib/dpkg/status");
1587
1588    /* if the list action was given print the installed packages and exit */
1589    if (dpkg_opt & dpkg_opt_list_installed) {
1590        list_packages();
1591        return(EXIT_SUCCESS);
1592    }
1593
1594    /* Read arguments and store relevant info in structs */
1595    while (optind < argc) {
1596        /* deb_count = nb_elem - 1 and we need nb_elem + 1 to allocate terminal node [NULL pointer] */
1597        deb_file = xrealloc(deb_file, sizeof(deb_file_t *) * (deb_count + 2));
1598        deb_file[deb_count] = (deb_file_t *) xzalloc(sizeof(deb_file_t));
1599        if (dpkg_opt & dpkg_opt_filename) {
1600            archive_handle_t *archive_handle;
1601            llist_t *control_list = NULL;
1602
1603            /* Extract the control file */
1604            llist_add_to(&control_list, "./control");
1605            archive_handle = init_archive_deb_ar(argv[optind]);
1606            init_archive_deb_control(archive_handle);
1607            deb_file[deb_count]->control_file = deb_extract_control_file_to_buffer(archive_handle, control_list);
1608            if (deb_file[deb_count]->control_file == NULL) {
1609                bb_error_msg_and_die("Couldnt extract control file");
1610            }
1611            deb_file[deb_count]->filename = bb_xstrdup(argv[optind]);
1612            package_num = fill_package_struct(deb_file[deb_count]->control_file);
1613
1614            if (package_num == -1) {
1615                bb_error_msg("Invalid control file in %s", argv[optind]);
1616                optind++;
1617                continue;
1618            }
1619            deb_file[deb_count]->package = (unsigned int) package_num;
1620
1621            /* Add the package to the status hashtable */
1622            if ((dpkg_opt & dpkg_opt_unpack) || (dpkg_opt & dpkg_opt_install)) {
1623                /* Try and find a currently installed version of this package */
1624                status_num = search_status_hashtable(name_hashtable[package_hashtable[deb_file[deb_count]->package]->name]);
1625                /* If no previous entry was found initialise a new entry */
1626                if ((status_hashtable[status_num] == NULL) ||
1627                    (status_hashtable[status_num]->status == 0)) {
1628                    status_node = (status_node_t *) xmalloc(sizeof(status_node_t));
1629                    status_node->package = deb_file[deb_count]->package;
1630                    /* reinstreq isnt changed to "ok" until the package control info
1631                     * is written to the status file*/
1632                    status_node->status = search_name_hashtable("install reinstreq not-installed");
1633                    status_hashtable[status_num] = status_node;
1634                } else {
1635                    set_status(status_num, "install", 1);
1636                    set_status(status_num, "reinstreq", 2);
1637                }
1638            }
1639        }
1640        else if (dpkg_opt & dpkg_opt_package_name) {
1641            deb_file[deb_count]->package = search_package_hashtable(
1642                search_name_hashtable(argv[optind]),
1643                search_name_hashtable("ANY"), VER_ANY);
1644            if (package_hashtable[deb_file[deb_count]->package] == NULL) {
1645                bb_error_msg_and_die("Package %s is uninstalled or unknown\n", argv[optind]);
1646            }
1647            package_num = deb_file[deb_count]->package;
1648            status_num = search_status_hashtable(name_hashtable[package_hashtable[package_num]->name]);
1649            state_status = get_status(status_num, 3);
1650
1651            /* check package status is "installed" */
1652            if (dpkg_opt & dpkg_opt_remove) {
1653                if ((strcmp(name_hashtable[state_status], "not-installed") == 0) ||
1654                    (strcmp(name_hashtable[state_status], "config-files") == 0)) {
1655                    bb_error_msg_and_die("%s is already removed.", name_hashtable[package_hashtable[package_num]->name]);
1656                }
1657                set_status(status_num, "deinstall", 1);
1658            }
1659            else if (dpkg_opt & dpkg_opt_purge) {
1660                /* if package status is "conf-files" then its ok */
1661                if (strcmp(name_hashtable[state_status], "not-installed") == 0) {
1662                    bb_error_msg_and_die("%s is already purged.", name_hashtable[package_hashtable[package_num]->name]);
1663                }
1664                set_status(status_num, "purge", 1);
1665            }
1666        }
1667        deb_count++;
1668        optind++;
1669    }
1670    deb_file[deb_count] = NULL;
1671
1672    /* Check that the deb file arguments are installable */
1673    if ((dpkg_opt & dpkg_opt_force_ignore_depends) != dpkg_opt_force_ignore_depends) {
1674        if (!check_deps(deb_file, 0, deb_count)) {
1675            bb_error_msg_and_die("Dependency check failed");
1676        }
1677    }
1678
1679    /* TODO: install or remove packages in the correct dependency order */
1680    for (i = 0; i < deb_count; i++) {
1681        /* Remove or purge packages */
1682        if (dpkg_opt & dpkg_opt_remove) {
1683            remove_package(deb_file[i]->package, 1);
1684        }
1685        else if (dpkg_opt & dpkg_opt_purge) {
1686            purge_package(deb_file[i]->package);
1687        }
1688        else if (dpkg_opt & dpkg_opt_unpack) {
1689            unpack_package(deb_file[i]);
1690        }
1691        else if (dpkg_opt & dpkg_opt_install) {
1692            unpack_package(deb_file[i]);
1693            /* package is configured in second pass below */
1694        }
1695        else if (dpkg_opt & dpkg_opt_configure) {
1696            configure_package(deb_file[i]);
1697        }
1698    }
1699    /* configure installed packages */
1700    if (dpkg_opt & dpkg_opt_install) {
1701        for (i = 0; i < deb_count; i++)
1702            configure_package(deb_file[i]);
1703    }
1704
1705    write_status_file(deb_file);
1706
1707    for (i = 0; i < deb_count; i++) {
1708        free(deb_file[i]->control_file);
1709        free(deb_file[i]->filename);
1710        free(deb_file[i]);
1711    }
1712
1713    free(deb_file);
1714
1715    for (i = 0; i < NAME_HASH_PRIME; i++) {
1716        free(name_hashtable[i]);
1717    }
1718
1719    for (i = 0; i < PACKAGE_HASH_PRIME; i++) {
1720        if (package_hashtable[i] != NULL) {
1721            free_package(package_hashtable[i]);
1722        }
1723    }
1724
1725    for (i = 0; i < STATUS_HASH_PRIME; i++) {
1726        free(status_hashtable[i]);
1727    }
1728
1729    return(EXIT_SUCCESS);
1730}
1731
Note: See TracBrowser for help on using the repository browser.