From 42ecff8eb985edba41f114c11d1df89329779c59 Mon Sep 17 00:00:00 2001 From: ewt Date: Sun, 14 Jan 1996 19:32:17 +0000 Subject: [PATCH] 1) checks for pacakges already installed, but doesn't handle overrides correctly 2) sets file ownderships by symbolic name when they are available 3) creates directories when needed CVS patchset: 207 CVS date: 1996/01/14 19:32:17 --- lib/install.c | 296 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 288 insertions(+), 8 deletions(-) diff --git a/lib/install.c b/lib/install.c index 13f8382..c6a6c86 100644 --- a/lib/install.c +++ b/lib/install.c @@ -1,8 +1,12 @@ #include #include +#include +#include +#include #include #include #include +#include /* needed for mkdir(2) prototype! */ #include #include #include @@ -14,28 +18,87 @@ #include "rpmerr.h" #include "rpmlib.h" -int installArchive(char * prefix, int fd); +static int installArchive(char * prefix, int fd); +static int packageAlreadyInstalled(rpmdb db, char * name, char * version, + char * release, int flags); +static int setFileOwnerships(char * prefix, char ** fileList, + char ** fileOwners, char ** fileGroups, + int fileCount); +static int setFileOwner(char * prefix, char * file, char * owner, + char * group); +static int createDirectories(char * prefix, char ** fileList, int fileCount); +static int mkdirIfNone(char * directory, mode_t perms); /* 0 success */ /* 1 bad magic */ /* 2 error */ -int rpmInstallPackage(char * prefix, rpmdb db, int fd, int test) { +int rpmInstallPackage(char * prefix, rpmdb db, int fd, int flags, int test) { int rc, isSource; + char * name, * version, * release; Header h; + int fileCount, type; + char ** fileList; + char ** fileOwners, ** fileGroups; rc = pkgReadHeader(fd, &h, &isSource); if (rc) return rc; + getEntry(h, RPMTAG_NAME, &type, (void **) &name, &fileCount); + getEntry(h, RPMTAG_VERSION, &type, (void **) &version, &fileCount); + getEntry(h, RPMTAG_RELEASE, &type, (void **) &release, &fileCount); + + message(MESS_DEBUG, "package: %s-%s-%s files test = %d\n", + name, version, release, test); + + if (packageAlreadyInstalled(db, name, version, release, flags)) { + freeHeader(h); + return 2; + } + + if (test) { + message(MESS_DEBUG, "stopping install as we're running --test\n"); + return 0; + } + message(MESS_DEBUG, "running preinstall script (if any)\n"); if (runScript(prefix, h, RPMTAG_PREIN)) { return 2; } - /* the file pointer for fd is pointing at the cpio archive */ - if (installArchive(prefix, fd)) { - freeHeader(h); - return 2; + if (getEntry(h, RPMTAG_FILENAMES, &type, (void **) &fileList, + &fileCount)) { + + if (createDirectories(prefix, fileList, fileCount)) { + freeHeader(h); + free(fileList); + return 2; + } + + /* the file pointer for fd is pointing at the cpio archive */ + if (installArchive(prefix, fd)) { + freeHeader(h); + free(fileList); + return 2; + } + + if (getEntry(h, RPMTAG_FILEUSERNAME, &type, (void **) &fileOwners, + &fileCount)) { + if (getEntry(h, RPMTAG_FILEGROUPNAME, &type, (void **) &fileGroups, + &fileCount)) { + if (setFileOwnerships(prefix, fileList, fileOwners, fileGroups, + fileCount)) { + free(fileOwners); + free(fileGroups); + free(fileList); + return 2; + } + free(fileGroups); + } + free(fileOwners); + } + free(fileList); } + message(MESS_DEBUG, "running postinstall script (if any)\n"); if (runScript(prefix, h, RPMTAG_POSTIN)) { @@ -52,7 +115,7 @@ int rpmInstallPackage(char * prefix, rpmdb db, int fd, int test) { #define BLOCKSIZE 1024 -int installArchive(char * prefix, int fd) { +static int installArchive(char * prefix, int fd) { gzFile stream; char buf[BLOCKSIZE]; pid_t child; @@ -84,7 +147,7 @@ int installArchive(char * prefix, int fd) { chdir(prefix); execl("/bin/cpio", "/bin/cpio", "--extract", "--verbose", "--unconditional", "--preserve-modification-time", - "--make-directories", "--quiet", "-t", NULL); + "--make-directories", "--quiet", /*"-t",*/ NULL); exit(-1); } @@ -112,3 +175,220 @@ int installArchive(char * prefix, int fd) { return 0; } + +static int packageAlreadyInstalled(rpmdb db, char * name, char * version, + char * release, int flags) { + char * secVersion, * secRelease; + Header sech; + int i; + dbIndexSet matches; + int type, count; + + if (!rpmdbFindPackage(db, name, &matches)) { + for (i = 0; i < matches.count; i++) { + sech = rpmdbGetRecord(db, matches.recs[i].recOffset); + if (!sech) { + return 1; + } + + getEntry(sech, RPMTAG_VERSION, &type, (void **) &secVersion, + &count); + getEntry(sech, RPMTAG_RELEASE, &type, (void **) &secRelease, + &count); + + freeHeader(sech); + + if (!strcmp(secVersion, version) && !strcmp(secRelease, release)) { + if (!(flags & INSTALL_REPLACEPKG)) { + error(RPMERR_PKGINSTALLED, + "package %s-%s-%s is already installed", + name, version, release); + return 1; + } + } + } + } + + return 0; +} + +static int setFileOwnerships(char * prefix, char ** fileList, + char ** fileOwners, char ** fileGroups, + int fileCount) { + int i; + + message(MESS_DEBUG, "setting file owners and groups by name (not id)"); + + for (i = 0; i < fileCount; i++) { + /* ignore errors here - setFileOwner handles them reasonable + and we want to keep running */ + setFileOwner(prefix, fileList[i], fileOwners[i], fileGroups[i]); + } + + return 0; +} + +/* setFileOwner() is really poorly implemented. It really ought to use + hash tables. I just made the guess that most files would be owned by + root or the same person/group who owned the last file. Those two values + are cached, everything else is looked up via getpw() and getgr() functions. + If this performs too poorly I'll have to implement it properly :-( */ + +static int setFileOwner(char * prefix, char * file, char * owner, + char * group) { + static char * lastOwner = NULL, * lastGroup = NULL; + static uid_t lastUID; + static gid_t lastGID; + uid_t uid = 0; + gid_t gid = 0; + struct passwd * pwent; + struct group * grent; + char * filespec; + + filespec = alloca(strlen(prefix) + strlen(file) + 5); + strcpy(filespec, prefix); + strcat(filespec, "/"); + strcat(filespec, file); + + if (!strcmp(owner, "root")) + uid = 0; + else if (lastOwner && !strcmp(lastOwner, owner)) + uid = lastUID; + else { + pwent = getpwnam(owner); + if (!pwent) { + error(RPMERR_NOUSER, "user %s does not exist - using root", owner); + uid = 0; + } else { + uid = pwent->pw_uid; + if (lastOwner) free(lastOwner); + lastOwner = strdup(owner); + lastUID = uid; + } + } + + if (!strcmp(group, "root")) + gid = 0; + else if (lastGroup && !strcmp(lastGroup, group)) + gid = lastGID; + else { + grent = getgrnam(group); + if (!grent) { + error(RPMERR_NOGROUP, "group %s does not exist - using root", + group); + gid = 0; + } else { + gid = grent->gr_gid; + if (lastGroup) free(lastGroup); + lastGroup = strdup(owner); + lastGID = gid; + } + } + + message(MESS_DEBUG, "%s owned by %s (%d), group %s (%d)\n", filespec, + owner, uid, group, gid); + if (chown(filespec, uid, gid)) { + error(RPMERR_CHOWN, "cannot set owner and group for %s - %s\n", + filespec, strerror(errno)); + return 1; + } + + return 0; +} + +/* This could be more efficient. A brute force tokenization and mkdir's + seems like horrible overkill. I did make it know better then trying + to create the same direc though. That should make it perform adequatally + thanks to the sorted filelist. + + This could create directories that should be symlinks :-( RPM building + should probably resolve symlinks in paths. + + This creates directories whose permissions are modified by the current + umask... I'm not sure how this should act */ + +static int createDirectories(char * prefix, char ** fileList, int fileCount) { + int i; + char * lastDirectory; + char * buffer; + int bufferLength; + int prefixLength = strlen(prefix); + int neededLength; + char * chptr; + + lastDirectory = malloc(1); + lastDirectory[0] = '\0'; + + bufferLength = 1000; /* should be more then adequate */ + buffer = malloc(bufferLength); + + for (i = 0; i < fileCount; i++) { + neededLength = prefixLength + 5 + strlen(fileList[i]); + if (neededLength > bufferLength) { + free(buffer); + bufferLength = neededLength * 2; + buffer = malloc(bufferLength); + } + strcpy(buffer, prefix); + strcat(buffer, "/"); + strcat(buffer, fileList[i]); + + for (chptr = buffer + strlen(buffer) - 1; *chptr; chptr--) { + if (*chptr == '/') break; + } + + if (! *chptr) continue; /* no path, just filename */ + if (chptr == buffer) continue; /* /filename - no directories */ + + *chptr = '\0'; /* buffer is now just directories */ + + if (!strcmp(buffer, lastDirectory)) continue; + + for (chptr = buffer + 1; *chptr; chptr++) { + if (*chptr == '/') { + *chptr = '\0'; + if (mkdirIfNone(buffer, 0755)) { + free(lastDirectory); + free(buffer); + return 1; + } + *chptr = '/'; + } + } + + if (mkdirIfNone(buffer, 0755)) { + free(lastDirectory); + free(buffer); + return 1; + } + + free(lastDirectory); + lastDirectory = strdup(buffer); + } + + free(lastDirectory); + free(buffer); + + return 0; +} + +static int mkdirIfNone(char * directory, mode_t perms) { + int rc; + char * chptr; + + /* if the path is '/' we get ENOFILE not found" from mkdir, rather + then EEXIST which is weird */ + for (chptr = directory; *chptr; chptr++) + if (*chptr != '/') break; + if (!*chptr) return 0; + + message(MESS_DEBUG, "trying to make %s\n", directory); + + rc = mkdir(directory, perms); + if (!rc || errno == EEXIST) return 0; + + error(RPMERR_MKDIR, "failed to create %s - %s\n", directory, + strerror(errno)); + + return errno; +} -- 2.7.4