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

Last change on this file since 1770 was 1770, checked in by Bruno Cornec, 13 years ago
  • Better output for mindi-busybox revision
  • Remove dummy file created on NFS - report from Arnaud Tiger <arnaud.tiger_at_hp.com>
  • strace useful for debug
  • fix new versions for pb (2.0.0 for mindi and 1.7.2 for mindi-busybox)
  • fix build process for mindi-busybox + options used in that version (dd for label-partitions-as-necessary)
  • fix typo in label-partitions-as-necessary which doesn't seem to work
  • Update to busybox 1.7.2
  • perl is now required at restore time to support uuid swap partitions (and will be used for many other thigs

in the future for sure)

  • next mindi version will be 2.0.0 due to all the changes made in it (udev may break working distros)
  • small optimization in mindi on keyboard handling (one single find instead of multiple)
  • better interaction for USB device when launching mindi manually
  • attempt to automatically guess block disk size for ramdisk
  • fix typos in bkphw
  • Fix the remaining problem with UUID support for swap partitions
  • Updates mondoarchive man page for USB support
  • Adds preliminary Hardware support to mindi (Proliant SSSTK)
  • Tries to add udev support also for rhel4
  • Fix UUID support which was still broken.
  • Be conservative in test for the start-nfs script
  • Update config file for mindi-busybox for 1.7.2 migration
  • Try to run around a busybox bug (1.2.2 pb on inexistant links)
  • Add build content for mindi-busybox in pb
  • Remove distributions content for mindi-busybox
  • Fix a warning on inexistant raidtab
  • Solve problem on tmpfs in restore init (Problem of inexistant symlink and busybox)
  • Create MONDO_CACHE and use it everywhere + creation at start
  • Really never try to eject a USB device
  • Fix a issue with &> usage (replaced with 1> and 2>)
  • Adds magic file to depllist in order to have file working + ldd which helps for debugging issues
  • tty modes correct to avoid sh error messages
  • Use ext3 normally and not ext2 instead
  • USB device should be corrected after reading (take 1st part)
  • Adds a mount_USB_here function derived from mount_CDROM_here
  • usb detection place before /dev detection in device name at restore time
  • Fix when restoring from USB: media is asked in interactive mode
  • Adds USB support for mondorestore
  • mount_cdrom => mount_media
  • elilo.efi is now searched throughout /boot/efi and not in a fixed place as there is no standard
  • untar-and-softlink => untar (+ interface change)
  • suppress useless softlinks creation/removal in boot process
  • avoids udevd messages on groups
  • Increase # of disks to 99 as in mindi at restore time (should be a conf file parameter)
  • skip existing big file creation
  • seems to work correctly for USB mindi boot
  • Adds group and tty link to udev conf
  • Always load usb-torage (even 2.6) to initiate USB bus discovery
  • Better printing of messages
  • Attempt to fix a bug in supporting OpenSusE 10.3 kernel for initramfs (mindi may now use multiple regex for kernel initrd detection)
  • Links were not correctly done as non relative for modules in mindi
  • exclusion of modules denied now works
  • Also create modules in their ordinary place, so that classical modprobe works + copy modules.dep
  • Fix bugs for DENY_MODS handling
  • Add device /dev/console for udev
  • ide-generic should now really be excluded
  • Fix a bug in major number for tty
  • If udev then adds modprobe/insmod to rootfs
  • tty0 is also cretaed with udev
  • ide-generic put rather in DENY_MODS
  • udevd remove from deplist s handled in mindi directly
  • better default for mindi when using --usb
  • Handles dynamically linked busybox (in case we want to use it soon ;-)
  • Adds fixed devices to create for udev
  • ide-generic should not be part of the initrd when using libata v2
  • support a dynamically linked udev (case on Ubuntu 7.10 and Mandriva 2008.0 so should be quite generic) This will give incitation to move to dyn. linked binaries in the initrd which will help for other tasks (ia6 4)
  • Improvement in udev support (do not use cl options not available in busybox)
  • Udev in mindi
    • auto creation of the right links at boot time with udev-links.conf(from Mandriva 2008.0)
    • rework startup of udev as current makes kernel crash (from Mandriva 2008.0)
    • add support for 64 bits udev
  • Try to render MyInsmod? silent at boot time
  • Adds udev support (mandatory for newest distributions to avoid remapping of devices in a different way as on the original system)
  • We also need vaft format support for USB boot
  • Adds libusual support (Ubuntu 7.10 needs it for USB)
  • Improve Ubuntu/Debian? keyboard detection and support
  • pbinit adapted to new pb (0.8.10). Filtering of docs done in it
  • Suppress some mondo warnings and errors on USB again
  • Tries to fix lack of files in deb mindi package
  • Verify should now work for USB devices
  • More log/mesages improvement for USB support
  • - Supress g_erase_tmpdir_and_scratchdir
  • Improve some log messages for USB support
  • Try to improve install in mindi to avoid issues with isolinux.cfg not installed vene if in the pkg :-(
  • Improve mindi-busybox build
  • In conformity with pb 0.8.9
  • Add support for Ubuntu 7.10 in build process
  • Add USB Key button to Menu UI (CD streamer removed)
  • Attempt to fix error messages on tmp/scratch files at the end by removing those dir at the latest possible.
  • Fix a bug linked to the size of the -E param which could be used (Arnaud Tiger/René? Ribaud).
  • Integrate ~/.pbrc content into mondorescue.pb (required project-builder >= 0.8.7)
  • Put mondorescue in conformity with new pb filtering rules
  • Add USB support at restore time (no test done yet). New start-usb script PB varibale added where useful
  • Unmounting USB device before removal of temporary scratchdir
  • Stil refining USB copy back to mondo (one command was not executed)
  • No need to have the image subdor in the csratchdir when USB.
  • umount the USB partition before attempting to use it
  • Remove useless copy from mindi to mondo at end of USB handling

(risky merge, we are raising the limits of 2 diverging branches. The status of stable is not completely sure as such. Will need lots of tests, but it's not yet done :-()
(merge -r1692:1769 $SVN_M/branches/2.2.5)

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