/* * GEM MINE - The ROCK Linux Package Manager * Copyright (C) 2002-2005 Clifford Wolf * * This program 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 program 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, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #include "mine.h" #include "md5sum.h" #include "memdb.h" /* we need to reverse the package file list so we first remove * the content of a directory and after that the directory itself. * so we temporarily store the data in a memdb. */ int compare_strings_reverse(char* left, char* right) { return strcmp(right, left); } int gem_remove(char * root, int mode_test, int mode_verbose, int mode_force, int mode_sub, char * package) { struct memdb_t flist_db; char buffer[1024], realfn1[1024], realfn2[1024]; char buf1[1024], buf2[1024]; char *filename; int sub_done = 0; int errors = 0; FILE *f; if ( ! mode_force ) md5sum_initdb(root, mode_verbose); if ( mode_sub && !strchr(package, ':') ) { DIR *d; snprintf(buffer, 1024, "%s/var/adm/flists", root); d = opendir(buffer); if ( d ) { int len = snprintf(buffer, 1024, "%s:", package); struct dirent *de; while ( (de = readdir(d)) != 0 ) { if ( !strncmp(de->d_name, buffer, len) ) { if ( mode_verbose ) printf("Removing sub-package of %s: %s\n", package, de->d_name); errors += gem_remove(root, mode_test, mode_verbose, mode_force, 0, de->d_name); sub_done = 1; } } closedir(d); } } snprintf(buffer, 1024, "%s/var/adm/flists/%s", root, package); f = fopen(buffer, "r"); if ( f == NULL ) { if ( sub_done ) return errors != 0; fprintf(stderr, "No such package: %s\n", package); return 1; } if ( mode_test && mode_verbose ) printf("-- %s --\n", package); memdb_init(&flist_db); while ( fgets(buffer, 1024, f) != NULL ) { strtok(buffer, " \t\n"); filename = strtok(NULL, "\n"); if (filename) { struct stat statbuf; char value; snprintf(realfn1, 1024, "%s/%s", root, filename); if ( lstat(realfn1, &statbuf) ) printf("WARNING: Could not stat file %s.\n", realfn1); if ( S_ISDIR(statbuf.st_mode) ) value = 1; else value = 0; memdb_put(&flist_db, filename, &value); } } fclose(f); FILE *logfile = NULL; if ( ! mode_test ) { char postinst[1024]; snprintf(postinst, 1024, "%s/var/adm/postinstall/%s-remove.XXXXXX", root, package); if ( mkstemp(postinst) != -1 ) logfile = fopen(postinst, "w"); if ( mode_verbose ) { if ( logfile == NULL ) printf("Not writing postremove log\n"); else printf("Writing postremove log to %s\n", postinst); } } struct memdb_entry_t *flist = flist_db.first; filename = 0; while ( flist != NULL ) { if(*(flist->value) == 1) { // directory, handeled later flist = flist->next; continue; } filename = flist->key; snprintf(realfn1, 1024, "%s/%s", root, filename); int result = 0; if ( ! mode_force && (result = md5sum_check(root, filename)) ) { if ( ! mode_test || ! mode_verbose ) printf("%s: ", package); printf("WARNING: Skip "); switch (result) { case MD5SUM_CHECK_DUPLICATE: printf("duplicate "); break; case MD5SUM_CHECK_MODIFIED: printf("modified "); break; case MD5SUM_CHECK_SHARED: printf("shared "); break; } printf("file %s:\n", filename); } else if ( mode_test ) { if ( ! mode_verbose ) printf("%s: ", package); printf("removing %s\n", filename); } else { if ( ! strncmp(filename, "var/adm/", 8) ) { if ( mode_verbose ) printf("%s: moving %s to var/adm/backup\n", package, filename); snprintf(realfn1, 1024, "%s/var/adm/backup", root); mkdir(realfn1, 0700); snprintf(realfn1, 1024, "%s", filename+8); sscanf(realfn1, "%[^/]/%[^/]", buf1, buf2); snprintf(realfn1, 1024, "%s/%s", root, filename); snprintf(realfn2, 1024, "%s/var/adm/backup/%s_%s", root, buf2, buf1); if ( rename(realfn1, realfn2) ) { printf("While removing package %s: %s: %s\n", package, realfn1, strerror(errno)); errors++; } } else { if ( mode_verbose ) printf("%s: removing %s\n", package, filename); if ( remove(realfn1) && errno != ENOTEMPTY ) { printf("While removing package %s: %s: %s\n", package, realfn1, strerror(errno)); errors++; } } if ( logfile != NULL ) fprintf(logfile, "%s: %s\n", package, filename); } flist = flist->next; memdb_remove(&flist_db, filename); } /* sort the remaining flist entries in reverse lexicographical order, so that /etc/mypackage/mysubdir is before /etc/mypackage. */ memdb_sort(&flist_db, compare_strings_reverse); // now check wether the left-over directories are empty flist = flist_db.first; while ( flist != NULL ) { snprintf(realfn1, 1024, "%s/%s", root, flist->key); DIR* dir = opendir(realfn1); if(dir) { struct dirent* de; int dir_not_empty = 0; while((de = readdir(dir))) { if(!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue; dir_not_empty = 1; break; } closedir(dir); if(!dir_not_empty) { if ( mode_verbose ) printf("%s: removing %s\n", package, realfn1); if ( remove(realfn1) && errno != ENOTEMPTY ) { printf("While removing package %s: %s: %s\n", package, realfn1, strerror(errno)); errors++; } } } flist = flist->next; } memdb_free(&flist_db); if ( logfile != NULL ) fclose(logfile); if ( errors ) fprintf(stderr, "%d error%s while removing package %s.\n", errors, errors != 1 ? "s" : "", package); return errors != 0; }