/******************************************************************************* Copyright(C) Jonas 'Sortie' Termansen 2015, 2016. 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 3 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, see . manifest.c Manifest handling functions. *******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include "execute.h" #include "fileops.h" #include "manifest.h" bool has_manifest(const char* manifest) { char* path; if ( asprintf(&path, "/tix/manifest/%s", manifest) < 0 ) { warn("asprintf"); _exit(2); } bool result = access(path, F_OK) == 0; free(path); return result; } struct hardlink { dev_t dev; ino_t ino; char* path; }; void install_manifest(const char* manifest, const char* from_prefix, const char* to_prefix) { printf(" - Installing %s...\n", manifest); struct hardlink* hardlinks = NULL; size_t hardlinks_used = 0; size_t hardlinks_length = 0; size_t buffer_size = 1 << 16; char* buffer = malloc(buffer_size); if ( !buffer ) { warn("malloc"); _exit(2); } mode_t old_umask = umask(0000); char* inmanifest; if ( asprintf(&inmanifest, "%s/tix/manifest/%s", from_prefix, manifest) < 0 ) { warn("asprintf"); _exit(2); } char* outmanifest; if ( asprintf(&outmanifest, "%s/tix/manifest/%s", to_prefix, manifest) < 0 ) { warn("asprintf"); _exit(2); } FILE* fpin = fopen(inmanifest, "r"); if ( !fpin ) { warn("%s", inmanifest); _exit(2); } FILE* fpout = fopen(outmanifest, "w"); if ( !fpout ) { warn("%s", outmanifest); _exit(2); } char* line = NULL; size_t line_size = 0; ssize_t line_length; while ( 0 <= (errno = 0, line_length = getline(&line, &line_size, fpin)) ) { if ( line_length && line[line_length-1] == '\n' ) line[--line_length] = '\0'; if ( fprintf(fpout, "%s\n", line) < 0 ) { warn("write: %s", outmanifest); _exit(2); } if ( line[0] != '/' ) continue; char* in_path; if ( asprintf(&in_path, "%s%s", from_prefix, line) < 0 ) { warn("asprintf"); _exit(2); } char* out_path = line; if ( asprintf(&out_path, "%s%s", to_prefix, line) < 0 ) { warn("asprintf"); _exit(2); } struct stat inst; if ( lstat(in_path, &inst) < 0 ) { warn("%s", in_path); _exit(2); } struct hardlink* hardlink = NULL; if ( S_ISREG(inst.st_mode) && 2 <= inst.st_nlink ) { for ( size_t i = 0; i < hardlinks_used; i++ ) { if ( hardlinks[i].dev != inst.st_dev || hardlinks[i].ino != inst.st_ino ) continue; hardlink = &hardlinks[i]; break; } } if ( hardlink ) { unlink(out_path); if ( link(hardlink->path, out_path) < 0 ) { warn("link: %s -> %s", hardlink->path, out_path); _exit(2); } } else if ( S_ISDIR(inst.st_mode) ) { if ( mkdir(out_path, inst.st_mode & 07777) < 0 && errno != EEXIST ) { warn("mkdir: %s", out_path); _exit(2); } } else if ( S_ISREG(inst.st_mode) ) { int in_fd = open(in_path, O_RDONLY); if ( in_fd < 0 ) { warn("%s", in_path); _exit(2); } unlink(out_path); int out_fd = open(out_path, O_WRONLY | O_CREAT | O_TRUNC, inst.st_mode & 07777); if ( out_fd < 0 ) { warn("%s", out_path); _exit(2); } while ( true ) { ssize_t amount = read(in_fd, buffer, buffer_size); if ( amount < 0 ) { warn("read: %s", in_path); _exit(2); } if ( amount == 0 ) break; if ( writeall(out_fd, buffer, (size_t) amount) < (size_t) amount ) { warn("write: %s", out_path); _exit(2); } } close(out_fd); close(in_fd); if ( 2 <= inst.st_nlink ) { if ( hardlinks_used == hardlinks_length ) { // TODO: Multiplication overflow. size_t new_length = hardlinks_length ? 2 * hardlinks_length : 16; struct hardlink* new_hardlinks = (struct hardlink*) reallocarray(hardlinks, new_length, sizeof(struct hardlink)); if ( !new_hardlinks ) { warn("malloc"); _exit(2); } hardlinks = new_hardlinks; hardlinks_length = new_length; } hardlinks[hardlinks_used].ino = inst.st_ino; hardlinks[hardlinks_used].dev = inst.st_dev; if ( !(hardlinks[hardlinks_used].path = strdup(out_path)) ) { warn("strdup"); _exit(2); } hardlinks_used++; } } else if ( S_ISLNK(inst.st_mode) ) { ssize_t amount = readlink(in_path, buffer, buffer_size - 1); if ( amount < 0 ) { warn("readlink: %s", in_path); _exit(2); } buffer[amount] = '\0'; unlink(out_path); if ( symlink(buffer, out_path) < 0 && errno != EEXIST ) { warn("symlink: %s", out_path); _exit(2); } } else { warnx("%s: Don't know how to copy this object", in_path); _exit(2); } free(in_path); free(out_path); } free(line); if ( errno ) { warn("%s", inmanifest); _exit(2); } fclose(fpin); if ( fclose(fpout) == EOF ) { warn("close: %s", outmanifest); _exit(2); } free(inmanifest); free(outmanifest); umask(old_umask); free(buffer); for ( size_t i = 0; i < hardlinks_used; i++ ) free(hardlinks[i].path); free(hardlinks); } bool check_installed(const char* path, const char* package) { FILE* fp = fopen(path, "r"); if ( !fp ) { if ( errno != ENOENT ) warn("%s", path); return false; } char* line = NULL; size_t line_size = 0; ssize_t line_length; while ( 0 < (errno = 0, line_length = getline(&line, &line_size, fp)) ) { if ( line[line_length-1] == '\n' ) line[--line_length] = '\0'; if ( !strcmp(line, package) ) { free(line); fclose(fp); return true; } } if ( errno != 0 ) warn("%s", path); free(line); fclose(fp); return false; } static char* shell_single_quote(const char* string) { char* result; size_t result_size; FILE* fp = open_memstream(&result, &result_size); if (!fp) return NULL; fputc('\'', fp); for ( size_t i = 0; string[i]; i++ ) { if ( string[i] == '\'' ) fputs("\'\\\'\'", fp); else fputc((unsigned char) string[i], fp); } fputc('\'', fp); fflush(fp); int waserr = ferror(fp); fclose(fp); if (waserr) { free(result); return NULL; } return result; } static char* sort_file_cmd(const char* file) { char* file_esc = shell_single_quote(file); if ( !file_esc ) return NULL; char* cmd; if ( asprintf(&cmd, "sort -- %s", file_esc) < 0 ) { free(file_esc); return NULL; } free(file_esc); return cmd; } void install_ports(const char* from_prefix, const char* to_prefix) { char* inst_in_path; char* inst_out_path; if ( asprintf(&inst_in_path, "%s/tix/installed.list", from_prefix) < 0 || asprintf(&inst_out_path, "%s/tix/installed.list", to_prefix) < 0 ) { warn("asprintf"); _exit(2); } if ( access_or_die(inst_in_path, F_OK) < 0 ) { free(inst_in_path); free(inst_out_path); return; } char* cmd = sort_file_cmd(inst_in_path); if ( !cmd ) { warn("sort_file_cmd"); _exit(2); } FILE* fp = popen(cmd, "r"); if ( !fp ) { warn("%s", cmd); _exit(2); } char* line = NULL; size_t line_size = 0; ssize_t line_length; while ( 0 < (errno = 0, line_length = getline(&line, &line_size, fp)) ) { if ( line[line_length-1] == '\n' ) line[--line_length] = '\0'; if ( !check_installed(inst_out_path, line) ) { FILE* inst_out_fp = fopen(inst_out_path, "a"); if ( !inst_out_fp || fprintf(inst_out_fp, "%s\n", line) < 0 || fflush(inst_out_fp) == EOF ) { warn("%s", inst_out_path); pclose(fp); _exit(2); } fclose(inst_out_fp); } char* tixinfo_in; char* tixinfo_out; if ( asprintf(&tixinfo_in, "%s/tix/tixinfo/%s", from_prefix, line) < 0 || asprintf(&tixinfo_out, "%s/tix/tixinfo/%s", to_prefix, line) < 0 ) { warn("asprintf"); pclose(fp); _exit(2); } execute((const char*[]) { "cp", "--", tixinfo_in, tixinfo_out, NULL }, "_e"); free(tixinfo_in); free(tixinfo_out); install_manifest(line, from_prefix, to_prefix); } free(line); if ( errno ) { warn("%s", cmd); pclose(fp); _exit(2); } pclose(fp); free(cmd); free(inst_in_path); free(inst_out_path); }