Work around glibc/nscd caching problems when doing 'rpm --root'. --- ./lib/rpmchroot.c.orig 2011-05-12 08:26:10.000000000 +0000 +++ ./lib/rpmchroot.c 2011-05-12 08:28:32.000000000 +0000 @@ -66,6 +66,7 @@ int rpmChrootIn(void) } else if (rootState.chrootDone == 0) { if (chdir("/") == 0 && chroot(rootState.rootDir) == 0) { rootState.chrootDone = 1; + rpmugChroot(1); } else { rpmlog(RPMLOG_ERR, _("Unable to change root directory: %m\n")); rc = -1; @@ -91,6 +92,7 @@ int rpmChrootOut(void) } else if (rootState.chrootDone == 1) { if (chroot(".") == 0 && fchdir(rootState.cwd) == 0) { rootState.chrootDone = 0; + rpmugChroot(0); } else { rpmlog(RPMLOG_ERR, _("Unable to restore root directory: %m\n")); rc = -1; --- ./lib/rpmug.c.orig 2011-05-12 08:13:52.000000000 +0000 +++ ./lib/rpmug.c 2011-05-12 08:33:28.000000000 +0000 @@ -35,6 +35,47 @@ const char * rpmugStashStr(const char *s return ret; } +#if defined(__GLIBC__) + +static int inchroot; + +/* + * Unfortunatelly glibc caches nss/nscd data and there is no + * good way to flush those caches when we did a chroot(). Thus + * we need to parse /etc/passwd and /etc/group ourselfs. + */ +static int safe_lookup(const char * file, const char * name) +{ + FILE *fp; + int l; + char buf[4096], *p; + + if (!name || !*name) + return -1; + l = strlen(name); + if ((fp = fopen(file, "r")) == 0) + return -1; + while ((p = fgets(buf, sizeof(buf), fp)) != 0) { + if (*p == '#') + continue; + while (*p && (*p == ' ' || *p == '\t')) + p++; + if (strncmp(p, name, l) != 0 || p[l] != ':') + continue; + p = strchr(p + l + 1, ':'); + if (!p) + continue; + fclose(fp); + p++; + while (*p && (*p == ' ' || *p == '\t')) + p++; + return atoi(p); + } + fclose(fp); + return -1; +} +#endif + /* * These really ought to use hash tables. I just made the * guess that most files would be owned by root or the same person/group @@ -68,17 +109,28 @@ int rpmugUid(const char * thisUname, uid lastUnameAlloced = thisUnameLen + 10; lastUname = xrealloc(lastUname, lastUnameAlloced); /* XXX memory leak */ } - strcpy(lastUname, thisUname); - pwent = getpwnam(thisUname); - if (pwent == NULL) { - /* FIX: shrug */ - endpwent(); +#if defined(__GLIBC__) + if (inchroot) { + int uid = safe_lookup("/etc/passwd", thisUname); + if (uid < 0) + return -1; + lastUid = uid; + } else +#endif + { pwent = getpwnam(thisUname); - if (pwent == NULL) return -1; + if (pwent == NULL) { + /* FIX: shrug */ + endpwent(); + pwent = getpwnam(thisUname); + if (pwent == NULL) return -1; + } + lastUid = pwent->pw_uid; } - lastUid = pwent->pw_uid; + strcpy(lastUname, thisUname); + lastUnameLen = thisUnameLen; } *uid = lastUid; @@ -111,18 +163,29 @@ int rpmugGid(const char * thisGname, gid lastGnameAlloced = thisGnameLen + 10; lastGname = xrealloc(lastGname, lastGnameAlloced); /* XXX memory leak */ } - strcpy(lastGname, thisGname); - grent = getgrnam(thisGname); - if (grent == NULL) { - /* FIX: shrug */ - endgrent(); +#if defined(__GLIBC__) + if (inchroot) { + int gid = safe_lookup("/etc/group", thisGname); + if (gid < 0) + return -1; + lastGid = gid; + } else +#endif + { grent = getgrnam(thisGname); if (grent == NULL) { - return -1; + /* FIX: shrug */ + endgrent(); + grent = getgrnam(thisGname); + if (grent == NULL) { + return -1; + } } + lastGid = grent->gr_gid; } - lastGid = grent->gr_gid; + strcpy(lastGname, thisGname); + lastGnameLen = thisGnameLen; } *gid = lastGid; @@ -134,7 +197,7 @@ const char * rpmugUname(uid_t uid) { static uid_t lastUid = (uid_t) -1; static char * lastUname = NULL; - static size_t lastUnameLen = 0; + static size_t lastUnameAlloced = 0; if (uid == (uid_t) -1) { lastUid = (uid_t) -1; @@ -151,9 +214,9 @@ const char * rpmugUname(uid_t uid) lastUid = uid; len = strlen(pwent->pw_name); - if (lastUnameLen < len + 1) { - lastUnameLen = len + 20; - lastUname = xrealloc(lastUname, lastUnameLen); + if (lastUnameAlloced < len + 1) { + lastUnameAlloced = len + 20; + lastUname = xrealloc(lastUname, lastUnameAlloced); } strcpy(lastUname, pwent->pw_name); @@ -165,7 +228,7 @@ const char * rpmugGname(gid_t gid) { static gid_t lastGid = (gid_t) -1; static char * lastGname = NULL; - static size_t lastGnameLen = 0; + static size_t lastGnameAlloced = 0; if (gid == (gid_t) -1) { lastGid = (gid_t) -1; @@ -182,9 +245,9 @@ const char * rpmugGname(gid_t gid) lastGid = gid; len = strlen(grent->gr_name); - if (lastGnameLen < len + 1) { - lastGnameLen = len + 20; - lastGname = xrealloc(lastGname, lastGnameLen); + if (lastGnameAlloced < len + 1) { + lastGnameAlloced = len + 20; + lastGname = xrealloc(lastGname, lastGnameAlloced); } strcpy(lastGname, grent->gr_name); @@ -200,3 +263,16 @@ void rpmugFree(void) rpmugGname(-1); strStash = strCacheFree(strStash); } + +void rpmugChroot(int in) +{ + /* tell libc to drop caches / file descriptors */ + endpwent(); + endgrent(); + /* drop our own caches */ + rpmugUid(NULL, NULL); + rpmugGid(NULL, NULL); +#if defined(__GLIBC__) + inchroot = in; +#endif +} --- ./lib/rpmug.h.orig 2011-05-12 08:13:52.000000000 +0000 +++ ./lib/rpmug.h 2011-05-12 08:26:56.000000000 +0000 @@ -15,4 +15,6 @@ const char * rpmugGname(gid_t gid); void rpmugFree(void); +void rpmugChroot(int in); + #endif /* _RPMUG_H */