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