int parsedb(const char *filename, enum parsedbflags flags, struct pkginfo **donep, FILE *warnto, int *warncount) { /* warnto, warncount and donep may be null. * If donep is not null only one package's information is expected. */ static int fd; struct pkginfo newpig, *pigp; struct pkginfoperfile *newpifp, *pifp; struct arbitraryfield *arp, **larpp; struct trigaw *ta; int pdone; int fieldencountered[array_count(fieldinfos)]; const struct fieldinfo *fip; const struct nickname *nick; char *data, *dataptr, *endptr; const char *fieldstart, *valuestart; char *value= NULL; int fieldlen= 0, valuelen= 0; int *ip, c; struct stat st; struct parsedb_state ps; ps.filename = filename; ps.flags = flags; ps.lno = 0; ps.warnto = warnto; ps.warncount = 0; newpifp= (flags & pdb_recordavailable) ? &newpig.available : &newpig.installed; fd= open(filename, O_RDONLY); if (fd == -1) ohshite(_("failed to open package info file `%.255s' for reading"),filename); push_cleanup(cu_closefd, ~ehflag_normaltidy, NULL, 0, 1, &fd); if (fstat(fd, &st) == -1) ohshite(_("can't stat package info file `%.255s'"),filename); if (st.st_size > 0) { #ifdef USE_MMAP dataptr = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); if (dataptr == MAP_FAILED) ohshite(_("can't mmap package info file `%.255s'"),filename); #else dataptr = m_malloc(st.st_size); fd_buf_copy(fd, dataptr, st.st_size, _("copy info file `%.255s'"), filename); #endif data= dataptr; endptr = dataptr + st.st_size; } else { data= dataptr= endptr= NULL; } pdone= 0; #define EOF_mmap(dataptr, endptr) (dataptr >= endptr) #define getc_mmap(dataptr) *dataptr++; #define ungetc_mmap(c, dataptr, data) dataptr--; for (;;) { /* loop per package */ memset(fieldencountered, 0, sizeof(fieldencountered)); blankpackage(&newpig); /* Skip adjacent new lines */ while(!EOF_mmap(dataptr, endptr)) { c= getc_mmap(dataptr); if (c!='\n' && c!=MSDOS_EOF_CHAR ) break; ps.lno++; } if (EOF_mmap(dataptr, endptr)) break; for (;;) { /* loop per field */ fieldstart= dataptr - 1; while (!EOF_mmap(dataptr, endptr) && !isspace(c) && c!=':' && c!=MSDOS_EOF_CHAR) c= getc_mmap(dataptr); fieldlen= dataptr - fieldstart - 1; while (!EOF_mmap(dataptr, endptr) && c != '\n' && isspace(c)) c= getc_mmap(dataptr); if (EOF_mmap(dataptr, endptr)) parse_error(&ps, &newpig, _("EOF after field name `%.*s'"), fieldlen, fieldstart); if (c == '\n') parse_error(&ps, &newpig, _("newline in field name `%.*s'"), fieldlen, fieldstart); if (c == MSDOS_EOF_CHAR) parse_error(&ps, &newpig, _("MSDOS EOF (^Z) in field name `%.*s'"), fieldlen, fieldstart); if (c != ':') parse_error(&ps, &newpig, _("field name `%.*s' must be followed by colon"), fieldlen, fieldstart); /* Skip space after ':' but before value and eol */ while(!EOF_mmap(dataptr, endptr)) { c= getc_mmap(dataptr); if (c == '\n' || !isspace(c)) break; } if (EOF_mmap(dataptr, endptr)) parse_error(&ps, &newpig, _("EOF before value of field `%.*s' (missing final newline)"), fieldlen,fieldstart); if (c == MSDOS_EOF_CHAR) parse_error(&ps, &newpig, _("MSDOS EOF char in value of field `%.*s' (missing newline?)"), fieldlen,fieldstart); valuestart= dataptr - 1; for (;;) { if (c == '\n' || c == MSDOS_EOF_CHAR) { ps.lno++; if (EOF_mmap(dataptr, endptr)) break; c= getc_mmap(dataptr); /* Found double eol, or start of new field */ if (EOF_mmap(dataptr, endptr) || c == '\n' || !isspace(c)) break; ungetc_mmap(c,dataptr, data); c= '\n'; } else if (EOF_mmap(dataptr, endptr)) { parse_error(&ps, &newpig, _("EOF during value of field `%.*s' (missing final newline)"), fieldlen,fieldstart); } c= getc_mmap(dataptr); } valuelen= dataptr - valuestart - 1; /* trim ending space on value */ while (valuelen && isspace(*(valuestart+valuelen-1))) valuelen--; for (nick = nicknames; nick->nick && (strncasecmp(nick->nick, fieldstart, fieldlen) || nick->nick[fieldlen] != '\0'); nick++) ; if (nick->nick) { fieldstart= nick->canon; fieldlen= strlen(fieldstart); } for (fip= fieldinfos, ip= fieldencountered; fip->name && strncasecmp(fieldstart,fip->name, fieldlen); fip++, ip++); if (fip->name) { value = m_realloc(value, valuelen + 1); memcpy(value,valuestart,valuelen); *(value + valuelen) = '\0'; if ((*ip)++) parse_error(&ps, &newpig, _("duplicate value for `%s' field"), fip->name); fip->rcall(&newpig, newpifp, &ps, value, fip); } else { if (fieldlen<2) parse_error(&ps, &newpig, _("user-defined field name `%.*s' too short"), fieldlen, fieldstart); larpp= &newpifp->arbs; while ((arp= *larpp) != NULL) { if (!strncasecmp(arp->name,fieldstart,fieldlen)) parse_error(&ps, &newpig, _("duplicate value for user-defined field `%.*s'"), fieldlen, fieldstart); larpp= &arp->next; } arp= nfmalloc(sizeof(struct arbitraryfield)); arp->name= nfstrnsave(fieldstart,fieldlen); arp->value= nfstrnsave(valuestart,valuelen); arp->next= NULL; *larpp= arp; } if (EOF_mmap(dataptr, endptr) || c == '\n' || c == MSDOS_EOF_CHAR) break; } /* loop per field */ if (pdone && donep) parse_error(&ps, &newpig, _("several package info entries found, only one allowed")); parse_must_have_field(&ps, &newpig, newpig.name, "package name"); if ((flags & pdb_recordavailable) || newpig.status != stat_notinstalled) { parse_ensure_have_field(&ps, &newpig, &newpifp->description, "description"); parse_ensure_have_field(&ps, &newpig, &newpifp->maintainer, "maintainer"); if (newpig.status != stat_halfinstalled) parse_must_have_field(&ps, &newpig, newpifp->version.version, "version"); } if (flags & pdb_recordavailable) parse_ensure_have_field(&ps, &newpig, &newpifp->architecture, "architecture"); /* Check the Config-Version information: * If there is a Config-Version it is definitely to be used, but * there shouldn't be one if the package is `installed' (in which case * the Version and/or Revision will be copied) or if the package is * `not-installed' (in which case there is no Config-Version). */ if (!(flags & pdb_recordavailable)) { if (newpig.configversion.version) { if (newpig.status == stat_installed || newpig.status == stat_notinstalled) parse_error(&ps, &newpig, _("Configured-Version for package with inappropriate Status")); } else { if (newpig.status == stat_installed) newpig.configversion= newpifp->version; } } if (newpig.trigaw.head && (newpig.status <= stat_configfiles || newpig.status >= stat_triggerspending)) parse_error(&ps, &newpig, _("package has status %s but triggers are awaited"), statusinfos[newpig.status].name); else if (newpig.status == stat_triggersawaited && !newpig.trigaw.head) parse_error(&ps, &newpig, _("package has status triggers-awaited but no triggers " "awaited")); if (!(newpig.status == stat_triggerspending || newpig.status == stat_triggersawaited) && newpig.trigpend_head) parse_error(&ps, &newpig, _("package has status %s but triggers are pending"), statusinfos[newpig.status].name); else if (newpig.status == stat_triggerspending && !newpig.trigpend_head) parse_error(&ps, &newpig, _("package has status triggers-pending but no triggers " "pending")); /* FIXME: There was a bug that could make a not-installed package have * conffiles, so we check for them here and remove them (rather than * calling it an error, which will do at some point). */ if (!(flags & pdb_recordavailable) && newpig.status == stat_notinstalled && newpifp->conffiles) { parse_warn(&ps, &newpig, _("Package which in state not-installed has conffiles, " "forgetting them")); newpifp->conffiles= NULL; } /* XXX: Mark not-installed leftover packages for automatic removal on * next database dump. This code can be removed after dpkg 1.16.x, when * there's guarantee that no leftover is found on the status file on * major distributions. */ if (!(flags & pdb_recordavailable) && newpig.status == stat_notinstalled && newpig.eflag == eflag_ok && (newpig.want == want_purge || newpig.want == want_deinstall || newpig.want == want_hold)) { newpig.want = want_unknown; } pigp= findpackage(newpig.name); pifp= (flags & pdb_recordavailable) ? &pigp->available : &pigp->installed; if ((flags & pdb_ignoreolder) && versioncompare(&newpifp->version, &pifp->version) < 0) continue; /* Copy the priority and section across, but don't overwrite existing * values if the pdb_weakclassification flag is set. */ if (newpig.section && *newpig.section && !((flags & pdb_weakclassification) && pigp->section && *pigp->section)) pigp->section= newpig.section; if (newpig.priority != pri_unknown && !((flags & pdb_weakclassification) && pigp->priority != pri_unknown)) { pigp->priority= newpig.priority; if (newpig.priority == pri_other) pigp->otherpriority= newpig.otherpriority; } /* Sort out the dependency mess. */ copy_dependency_links(pigp,&pifp->depends,newpifp->depends, (flags & pdb_recordavailable) ? 1 : 0); /* Leave the `depended' pointer alone, we've just gone to such * trouble to get it right :-). The `depends' pointer in * pifp was indeed also updated by copy_dependency_links, * but since the value was that from newpifp anyway there's * no need to copy it back. */ newpifp->depended= pifp->depended; /* Copy across data */ memcpy(pifp,newpifp,sizeof(struct pkginfoperfile)); if (!(flags & pdb_recordavailable)) { pigp->want= newpig.want; pigp->eflag= newpig.eflag; pigp->status= newpig.status; pigp->configversion= newpig.configversion; pigp->files= NULL; pigp->trigpend_head = newpig.trigpend_head; pigp->trigaw = newpig.trigaw; for (ta = pigp->trigaw.head; ta; ta = ta->sameaw.next) { assert(ta->aw == &newpig); ta->aw = pigp; /* ->othertrigaw_head is updated by trig_note_aw in *(findpackage()) * rather than in newpig */ } } else if (!(flags & pdb_ignorefiles)) { pigp->files= newpig.files; } if (donep) *donep= pigp; pdone++; if (EOF_mmap(dataptr, endptr)) break; if (c == '\n') ps.lno++; } if (data != NULL) { #ifdef USE_MMAP munmap(data, st.st_size); #else free(data); #endif } free(value); pop_cleanup(ehflag_normaltidy); if (close(fd)) ohshite(_("failed to close after read: `%.255s'"),filename); if (donep && !pdone) ohshit(_("no package information in `%.255s'"),filename); if (warncount) *warncount = ps.warncount; return pdone; }
void ensure_statoverrides(void) { static struct varbuf vb; struct stat stab1, stab2; FILE *file; char *loaded_list, *loaded_list_end, *thisline, *nextline, *ptr; struct file_stat *fso; struct filenamenode *fnn; varbufreset(&vb); varbufaddstr(&vb, admindir); varbufaddstr(&vb, "/" STATOVERRIDEFILE); varbufaddc(&vb, 0); onerr_abort++; file = fopen(vb.buf,"r"); if (!file) { if (errno != ENOENT) ohshite(_("failed to open statoverride file")); if (!statoverridefile) { onerr_abort--; return; } } else { if (fstat(fileno(file), &stab2)) ohshite(_("failed to fstat statoverride file")); if (statoverridefile) { if (fstat(fileno(statoverridefile), &stab1)) ohshite(_("failed to fstat previous statoverride file")); if (stab1.st_dev == stab2.st_dev && stab1.st_ino == stab2.st_ino) { fclose(file); onerr_abort--; return; } } } if (statoverridefile) fclose(statoverridefile); statoverridefile = file; setcloexec(fileno(statoverridefile), vb.buf); /* If the statoverride list is empty we don't need to bother * reading it. */ if (!stab2.st_size) { onerr_abort--; return; } loaded_list = nfmalloc(stab2.st_size); loaded_list_end = loaded_list + stab2.st_size; fd_buf_copy(fileno(file), loaded_list, stab2.st_size, _("statoverride file `%.250s'"), vb.buf); thisline = loaded_list; while (thisline < loaded_list_end) { fso = nfmalloc(sizeof(struct file_stat)); if (!(ptr = memchr(thisline, '\n', loaded_list_end - thisline))) ohshit(_("statoverride file is missing final newline")); /* Where to start next time around. */ nextline = ptr + 1; if (ptr == thisline) ohshit(_("statoverride file contains empty line")); *ptr = '\0'; /* Extract the uid. */ if (!(ptr = memchr(thisline, ' ', nextline - thisline))) ohshit(_("syntax error in statoverride file")); *ptr = '\0'; fso->uid = statdb_parse_uid(thisline); /* Move to the next bit */ thisline = ptr + 1; if (thisline >= loaded_list_end) ohshit(_("unexpected end of line in statoverride file")); /* Extract the gid */ if (!(ptr = memchr(thisline, ' ', nextline - thisline))) ohshit(_("syntax error in statoverride file")); *ptr = '\0'; fso->gid = statdb_parse_gid(thisline); /* Move to the next bit */ thisline = ptr + 1; if (thisline >= loaded_list_end) ohshit(_("unexpected end of line in statoverride file")); /* Extract the mode */ if (!(ptr = memchr(thisline, ' ', nextline - thisline))) ohshit(_("syntax error in statoverride file")); *ptr = '\0'; fso->mode = statdb_parse_mode(thisline); /* Move to the next bit */ thisline = ptr + 1; if (thisline >= loaded_list_end) ohshit(_("unexpected end of line in statoverride file")); fnn = findnamenode(thisline, 0); if (fnn->statoverride) ohshit(_("multiple statusoverrides present for file '%.250s'"), thisline); fnn->statoverride = fso; /* Moving on.. */ thisline = nextline; } onerr_abort--; }