diff options
Diffstat (limited to 'lib/dpkg/db-fsys-override.c')
-rw-r--r-- | lib/dpkg/db-fsys-override.c | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/lib/dpkg/db-fsys-override.c b/lib/dpkg/db-fsys-override.c new file mode 100644 index 0000000..b74f6cb --- /dev/null +++ b/lib/dpkg/db-fsys-override.c @@ -0,0 +1,268 @@ +/* + * libdpkg - Debian packaging suite library routines + * db-fsys-override.c - management of filesystem stat overrides database + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2000, 2001 Wichert Akkerman <wakkerma@debian.org> + * Copyright © 2008-2012 Guillem Jover <guillem@debian.org> + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> +#include <string.h> +#include <pwd.h> +#include <grp.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> + +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/fdio.h> +#include <dpkg/debug.h> +#include <dpkg/db-fsys.h> + +static char *statoverridename; + +uid_t +statdb_parse_uid(const char *str) +{ + char *endptr; + uid_t uid; + + if (str[0] == '#') { + long int value; + + errno = 0; + value = strtol(str + 1, &endptr, 10); + if (str + 1 == endptr || *endptr || value < 0 || errno != 0) + ohshit(_("invalid statoverride uid %s"), str); + uid = (uid_t)value; + } else { + const struct passwd *pw = getpwnam(str); + + if (pw == NULL) + uid = (uid_t)-1; + else + uid = pw->pw_uid; + } + + return uid; +} + +gid_t +statdb_parse_gid(const char *str) +{ + char *endptr; + gid_t gid; + + if (str[0] == '#') { + long int value; + + errno = 0; + value = strtol(str + 1, &endptr, 10); + if (str + 1 == endptr || *endptr || value < 0 || errno != 0) + ohshit(_("invalid statoverride gid %s"), str); + gid = (gid_t)value; + } else { + const struct group *gr = getgrnam(str); + + if (gr == NULL) + gid = (gid_t)-1; + else + gid = gr->gr_gid; + } + + return gid; +} + +mode_t +statdb_parse_mode(const char *str) +{ + char *endptr; + long int mode; + + mode = strtol(str, &endptr, 8); + if (str == endptr || *endptr || mode < 0 || mode > 07777) + ohshit(_("invalid statoverride mode %s"), str); + + return (mode_t)mode; +} + +void +ensure_statoverrides(enum statdb_parse_flags flags) +{ + static struct stat sb_prev; + struct stat sb_next; + static FILE *file_prev; + FILE *file; + char *loaded_list, *loaded_list_end, *thisline, *nextline, *ptr; + struct file_stat *fso; + struct fsys_namenode *fnn; + struct fsys_hash_iter *iter; + + if (statoverridename == NULL) + statoverridename = dpkg_db_get_path(STATOVERRIDEFILE); + + onerr_abort++; + + file = fopen(statoverridename, "r"); + if (!file) { + if (errno != ENOENT) + ohshite(_("failed to open statoverride file")); + } else { + setcloexec(fileno(file), statoverridename); + + if (fstat(fileno(file), &sb_next)) + ohshite(_("failed to fstat statoverride file")); + + /* + * We need to keep the database file open so that the + * filesystem cannot reuse the inode number (f.ex. during + * multiple dpkg-statoverride invocations in a maintainer + * script), otherwise the following check might turn true, + * and we would skip reloading a modified database. + */ + if (file_prev && + sb_prev.st_dev == sb_next.st_dev && + sb_prev.st_ino == sb_next.st_ino) { + fclose(file); + onerr_abort--; + debug(dbg_general, "%s: same, skipping", __func__); + return; + } + sb_prev = sb_next; + } + if (file_prev) + fclose(file_prev); + file_prev = file; + + /* Reset statoverride information. */ + iter = fsys_hash_iter_new(); + while ((fnn = fsys_hash_iter_next(iter))) + fnn->statoverride = NULL; + fsys_hash_iter_free(iter); + + if (!file) { + onerr_abort--; + debug(dbg_general, "%s: none, resetting", __func__); + return; + } + debug(dbg_general, "%s: new, (re)loading", __func__); + + /* If the statoverride list is empty we don't need to bother + * reading it. */ + if (!sb_next.st_size) { + onerr_abort--; + return; + } + + loaded_list = m_malloc(sb_next.st_size); + loaded_list_end = loaded_list + sb_next.st_size; + + if (fd_read(fileno(file), loaded_list, sb_next.st_size) < 0) + ohshite(_("reading statoverride file '%.250s'"), statoverridename); + + thisline = loaded_list; + while (thisline < loaded_list_end) { + fso = nfmalloc(sizeof(*fso)); + + ptr = memchr(thisline, '\n', loaded_list_end - thisline); + if (ptr == NULL) + 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. */ + ptr = memchr(thisline, ' ', nextline - thisline); + if (ptr == NULL) + ohshit(_("syntax error in statoverride file")); + *ptr = '\0'; + + fso->uid = statdb_parse_uid(thisline); + if (fso->uid == (uid_t)-1) + fso->uname = nfstrsave(thisline); + else + fso->uname = NULL; + + if (fso->uid == (uid_t)-1 && !(flags & STATDB_PARSE_LAX)) + ohshit(_("unknown system user '%s' in statoverride file; the system user got removed\n" + "before the override, which is most probably a packaging bug, to recover you\n" + "can remove the override manually with %s"), thisline, DPKGSTAT); + + /* Move to the next bit */ + thisline = ptr + 1; + if (thisline >= loaded_list_end) + ohshit(_("unexpected end of line in statoverride file")); + + /* Extract the gid */ + ptr = memchr(thisline, ' ', nextline - thisline); + if (ptr == NULL) + ohshit(_("syntax error in statoverride file")); + *ptr = '\0'; + + fso->gid = statdb_parse_gid(thisline); + if (fso->gid == (gid_t)-1) + fso->gname = nfstrsave(thisline); + else + fso->gname = NULL; + + if (fso->gid == (gid_t)-1 && !(flags & STATDB_PARSE_LAX)) + ohshit(_("unknown system group '%s' in statoverride file; the system group got removed\n" + "before the override, which is most probably a packaging bug, to recover you\n" + "can remove the override manually with %s"), thisline, DPKGSTAT); + + /* Move to the next bit */ + thisline = ptr + 1; + if (thisline >= loaded_list_end) + ohshit(_("unexpected end of line in statoverride file")); + + /* Extract the mode */ + ptr = memchr(thisline, ' ', nextline - thisline); + if (ptr == NULL) + 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 = fsys_hash_find_node(thisline, FHFF_NONE); + if (fnn->statoverride) + ohshit(_("multiple statoverrides present for file '%.250s'"), + thisline); + fnn->statoverride = fso; + + /* Moving on... */ + thisline = nextline; + } + + free(loaded_list); + + onerr_abort--; +} |