/* * Copyright (C) 2008 Kristian Høgsberg * Copyright (C) 2008 Red Hat, Inc * Copyright (C) 2009 J. Ali Harlow * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #ifdef MSWIN_API #include #include #endif #if HAVE_SYS_MMAN_H #include #endif #include #include "razor.h" #include "razor-internal.h" #ifndef O_BINARY #define O_BINARY 0 #endif #define RAZOR_ASCII_ISALPHA(c) \ ((c) >= 'A' && (c) <= 'Z' || (c) >= 'a' && (c) <= 'z') /* Required by gnulib on non-libc platforms */ char *program_name = "librazor"; static int allow_all_root_names = 0; /* * Primarily intended for testing named roots under UNIX platforms. */ RAZOR_EXPORT void razor_disable_root_name_checks(int disable) { allow_all_root_names = disable; } static int razor_valid_root_name(const char *name) { if (allow_all_root_names) return !strchr(name,'/'); #ifdef MSWIN_API return RAZOR_ASCII_ISALPHA(name[0]) && name[1] == ':' && name[2] == '\0'; #else return name[0] == '\0'; #endif } int razor_create_dir(const char *root, const char *path) { char buffer[PATH_MAX], *p; const char *slash, *next; struct stat buf; /* Create all sub-directories in dir. We know root exists and * is a dir, root does not end in a '/', and path either has a * leading '/' or (on MS-Windows only) root is the empty string * and path starts with drive (eg., "c:/windows"). */ strcpy(buffer, root); p = buffer + strlen(buffer); slash = path; for (slash = path; *slash != '\0'; slash = next) { next = strchr(slash + 1, '/'); if (next == NULL) break; memcpy(p, slash, next - slash); p += next - slash; *p = '\0'; if (razor_valid_root_name(buffer)) continue; if (stat(buffer, &buf) == 0) { if (!S_ISDIR(buf.st_mode)) { fprintf(stderr, "%s exists but is not a directory\n", buffer); return -1; } } else if (mkdir(buffer, 0777) < 0) { fprintf(stderr, "failed to make directory %s: %s\n", buffer, strerror(errno)); return -1; } /* FIXME: What to do about permissions for dirs we * have to create but are not in the cpio archive? */ } return 0; } int razor_remove(const char *path) { #ifdef MSWIN_API DWORD err; if (DeleteFile(path)) return 0; err = GetLastError(); if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) return 0; if (SetFileAttributes(path, FILE_ATTRIBUTE_NORMAL) && DeleteFile(path)) return 0; if (RemoveDirectory(path) || GetLastError() == ERROR_DIR_NOT_EMPTY) return 0; /* * It would be tempting to use: * MoveFileEx(path, NULL, MOVEFILE_DELAY_UNTIL_REBOOT) * but unless we can guarantee that the system will be rebooted * before we (or some other application) write another file with the * same path, this is likely to cause more problems than it solves. */ /* Use remove() as a fallback so that errno is set appropriately */ #endif return remove(path); } int razor_write(int fd, const void *data, size_t size) { size_t rest; ssize_t written; const unsigned char *p; rest = size; p = data; while (rest > 0) { written = write(fd, p, rest); if (written < 0) { perror("write error"); return -1; } rest -= written; p += written; } return 0; } void * razor_file_get_contents(const char *filename, size_t *length) { int fd; struct stat st; void *addr; #if !HAVE_SYS_MMAN_H size_t nb; ssize_t res; #endif fd = open(filename, O_RDONLY | O_BINARY); if (fd < 0) return NULL; if (fstat(fd, &st) < 0) { close(fd); return NULL; } *length = st.st_size; #if HAVE_SYS_MMAN_H addr = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); #else addr = malloc(st.st_size); if (addr) { nb = 0; while(nb < st.st_size) { res = read(fd, addr + nb, st.st_size - nb); if (res <= 0) { free(addr); addr = NULL; break; } nb += res; } } #endif close(fd); #if HAVE_SYS_MMAN_H if (addr == MAP_FAILED) addr = NULL; #endif return addr; } int razor_file_free_contents(void *addr, size_t length) { #if HAVE_SYS_MMAN_H return munmap(addr, length); #else free(addr); return 0; #endif } struct qsort_context { size_t size; razor_compare_with_data_func_t compare; void *data; }; static void qsort_swap(void *p1, void *p2, size_t size) { char buffer[size]; memcpy(buffer, p1, size); memcpy(p1, p2, size); memcpy(p2, buffer, size); } static void __qsort_with_data(void *base, size_t nelem, uint32_t *map, struct qsort_context *ctx) { void *p, *start, *end, *pivot; uint32_t *mp, *mstart, *mend, tmp; int left, right, result; size_t size = ctx->size; p = base; start = base; end = base + nelem * size; mp = map; mstart = map; mend = map + nelem; pivot = base + (rand() % nelem) * size; while (p < end) { result = ctx->compare(p, pivot, ctx->data); if (result < 0) { qsort_swap(p, start, size); tmp = *mp; *mp = *mstart; *mstart = tmp; if (start == pivot) pivot = p; start += size; mstart++; p += size; mp++; } else if (result == 0) { p += size; mp++; } else { end -= size; mend--; qsort_swap(p, end, size); tmp = *mp; *mp = *mend; *mend = tmp; if (end == pivot) pivot = p; } } left = (start - base) / size; right = (base + nelem * size - end) / size; if (left > 1) __qsort_with_data(base, left, map, ctx); if (right > 1) __qsort_with_data(end, right, mend, ctx); } uint32_t * razor_qsort_with_data(void *base, size_t nelem, size_t size, razor_compare_with_data_func_t compare, void *data) { struct qsort_context ctx; uint32_t *map; int i; if (nelem == 0) return NULL; ctx.size = size; ctx.compare = compare; ctx.data = data; map = malloc(nelem * sizeof (uint32_t)); for (i = 0; i < nelem; i++) map[i] = i; __qsort_with_data(base, nelem, map, &ctx); return map; } void environment_init(struct environment *env) { env->is_set = 0; array_init(&env->string_pool); array_init(&env->vars); } void environment_add_variable(struct environment *env, const char *variable, const char *value) { char *s; uint32_t *r; assert(!env->is_set); s = array_add(&env->string_pool, strlen(variable) + strlen(value) + 2); sprintf(s, "%s=%s", variable, value); r = array_add(&env->vars, sizeof *r); *r = s - (char *)env->string_pool.data; } void environment_set(struct environment *env) { int i, count; char *s; uint32_t *r; if (!env->is_set) { count = env->vars.size / sizeof(uint32_t); r = (uint32_t *)env->vars.data; for (i = 0; i < count; i++) { s = env->string_pool.data + *r++; putenv(s); } env->is_set = 1; } } void environment_unset(struct environment *env) { int i, count; char c, *s, *t; uint32_t *r; if (env->is_set) { count = env->vars.size / sizeof(uint32_t); r = (uint32_t *)env->vars.data; for (i = 0; i < count; i++) { s = env->string_pool.data + *r++; t = strchr(s, '=') + 1; c = *t; *t = '\0'; putenv(s); *t = c; } env->is_set = 0; } } void environment_release(struct environment *env) { environment_unset(env); array_release(&env->string_pool); array_release(&env->vars); }