/* apply-changeset.c: * * vim:smartindent ts=8:sts=2:sta:et:ai:shiftwidth=2 **************************************************************** * Copyright (C) 2003 Tom Lord * * See the file "COPYING" for further information about * the copyright and warranty status of this work. */ #include "config-options.h" #include "hackerlab/bugs/exception.h" #include "hackerlab/bugs/panic.h" #include "hackerlab/os/errno.h" #include "hackerlab/os/errno-to-string.h" #include "hackerlab/os/sys/types.h" #include "hackerlab/os/sys/wait.h" #include "hackerlab/os/signal.h" #include "hackerlab/mem/mem.h" #include "hackerlab/mem/talloc.h" #include "hackerlab/arrays/ar.h" #include "hackerlab/char/str.h" #include "hackerlab/fmt/cvt.h" #include "hackerlab/fs/file-names.h" #include "hackerlab/fs/cwd.h" #include "hackerlab/vu/safe.h" #include "hackerlab/char/pika-escaping-utils.h" #include "libfsutils/link-target.h" #include "libfsutils/read-line.h" #include "libfsutils/tmp-files.h" #include "libfsutils/rmrf.h" #include "libfsutils/ensure-dir.h" #include "libfsutils/copy-file.h" #include "libarch/diffs.h" #include "libarch/proj-tree-lint.h" #include "libawk/associative.h" #include "libawk/relassoc.h" #include "libawk/numbers.h" #include "libarch/changelogs.h" #include "libarch/conflict-handling.h" #include "libarch/debug.h" #include "libarch/project-tree.h" #include "libarch/exec.h" #include "libarch/apply-changeset.h" typedef struct apply_changeset_ { arch_apply_changeset_report_callback callback; void * thunk; int escape_classes; struct arch_apply_changeset_report * report; int here_fd; struct arch_changeset_report changeset; assoc_table mod_dir_id_of; assoc_table new_dir_perms; t_uchar * removed_patch_conflict_files_path; rel_table deferred_conflicts; /* [0] final target location */ struct running_inventory_assocs running; arch_project_tree_t * target; } apply_changeset_t; /* __STDC__ prototypes for static functions */ static int run_patch (int apply_in_reverse, int forward_opt_to_patch, char * patch_path, char * tree_file_base, char * original_inode_name); static int dir_depth_cmp (t_uchar * a, t_uchar * b); static int run_diff3 (t_uchar * basename, t_uchar * mine_path, t_uchar * older_path, t_uchar * yours_path); static void invoke_apply_changeset_callback (apply_changeset_t * apply, t_uchar * fmt, ...); static void one_path_callback (apply_changeset_t * apply, t_uchar * fmt, t_uchar *path); static void two_path_callback (apply_changeset_t * apply, t_uchar * fmt, t_uchar *path1, t_uchar *path2); static t_uchar * set_aside_shuffled_dirs (rel_table * file_set_aside_with, rel_table * dir_set_aside_with, t_uchar * target_loc, t_uchar * id, int seq_n, t_uchar * dest_root, struct running_inventory_assocs * running, t_uchar * target, struct arch_changeset_inventory * inv); static int analyze_install (struct running_inventory_assocs * running, int is_dir, int is_rename, t_uchar ** target_has_dir, t_uchar ** install_dir, t_uchar ** install_name, t_uchar ** install_loc, t_uchar * target, t_uchar * mod_loc, t_uchar * id, assoc_table mod_dir_id_of, rel_table file_set_aside_with, rel_table dir_set_aside_with); static void ensure_directory_eliminating_conflicts (apply_changeset_t * apply, t_uchar * target_has_path, t_uchar * dir); static int deferred_conflict (apply_changeset_t * apply, t_uchar * loc, t_uchar * orig_copy, struct running_inventory_assocs *inventory); static t_uchar * rename_removed_files(void * context, arch_project_tree_t * tree, apply_changeset_t * apply, struct running_inventory_assocs * running); static rel_table find_target_equivalent (rel_table source, rel_table target); static rel_table find_target_missing(rel_table source, rel_table target); static int arch_free_apply_changeset_report_data (void * data); static void identify_removed_and_missing_removed_files ( struct arch_changeset_report * changeset, struct arch_changeset_inventory * inventory, struct arch_apply_changeset_report * r); static void report_sort_and_unique (struct arch_apply_changeset_report * r); static apply_changeset_t * arch_apply_changeset_new (void * context, arch_apply_changeset_report_callback callback, void * thunk, int escape_classes, arch_project_tree_t * target); static int apply_changeset_destructor (void * data); static void read_changeset (apply_changeset_t * apply, t_uchar const * changeset_spec); static void preserve_old_patch_spew (t_uchar * dest_root, arch_project_tree_t * target, t_uchar * loc, struct running_inventory_assocs *inventory); static void setup_running (apply_changeset_t * apply, struct arch_changeset_inventory * inventory); int arch_conflicts_occurred (struct arch_apply_changeset_report * r) { return (r->conflict_files || r->conflict_dirs || r->metadata_conflict_files || r->metadata_conflict_dirs); } int arch_free_apply_changeset_report_data (void * data) { struct arch_apply_changeset_report * r = talloc_get_type (data, struct arch_apply_changeset_report); invariant (!!r); rel_free_table (r->removed_dirs); rel_free_table (r->missing_removed_files); rel_free_table (r->missing_removed_dirs); rel_free_table (r->missing_renamed_files); rel_free_table (r->missing_renamed_dirs); rel_free_table (r->new_dirs); rel_free_table (r->renamed_dirs); rel_free_table (r->new_files); rel_free_table (r->renamed_files); rel_free_table (r->modified_files); rel_free_table (r->modified_dirs); rel_free_table (r->missing_file_for_patch); rel_free_table (r->missing_dir_for_patch); rel_free_table (r->meta_modified_files); rel_free_table (r->meta_modified_dirs); rel_free_table (r->missing_file_for_meta_patch); rel_free_table (r->missing_dir_for_meta_patch); rel_free_table (r->conflict_files); rel_free_table (r->conflict_dirs); rel_free_table (r->metadata_conflict_files); rel_free_table (r->metadata_conflict_dirs); return 0; } struct arch_apply_changeset_report * arch_apply_changeset (t_uchar * changeset_spec, void * context, arch_project_tree_t * real_target_tree, enum arch_id_tagging_method method, enum arch_inventory_category untagged_source_category, int reverse, int forward, int escape_classes, arch_apply_changeset_report_callback callback, void * thunk) { apply_changeset_t * apply = arch_apply_changeset_new (talloc_context, callback, thunk, escape_classes, real_target_tree); struct arch_apply_changeset_report * result; int x; t_uchar * missing_patch_dir = 0; struct arch_changeset_inventory inventory; struct arch_changeset_inventory inventory_by_name; t_uchar * tmp_removed_files_root = 0; rel_table renamed_files_index = 0; rel_table present_renamed_files_index = 0; /* [0] tgtloc [1] origloc [2] modloc [3] id */ t_uchar * tmp_renamed_files_root = 0; rel_table renamed_dirs_index = 0; rel_table present_renamed_dirs_index = 0; /* [0] tgtloc [1] origloc [2] modloc [3] id [4] tmp_name (sort -r [0])*/ rel_table removed_dirs_index = 0; rel_table present_removed_dirs_index = 0; /* [0] tgtloc [1] id [2] tmp_name (sort -r [0]) */ t_uchar * tmp_shuffled_dirs_root = 0; rel_table dir_set_aside_with_dir_id = 0; /* [0] shuffled-dir-id [1] rel-loc-in-shuffled-dir [2] id */ rel_table file_set_aside_with_dir_id = 0; /* [0] shuffled-dir-id [1] rel-loc-in-shuffled-dir [2] id */ rel_table install_dirs_plan = 0; /* [0] modloc [1] path-or-empty-str [2] id [3] oldtgtloc */ rel_table added_files_and_symlinks = 0; rel_table patched_changelogs = 0; /* [0] final-loc [1] id [2] target_path */ read_changeset (apply, changeset_spec); missing_patch_dir = tmp_seq_file (apply->target->root, "++patches-missing-files"); /**************************************************************** * Study the changeset. */ if (reverse) { arch_reverse_changeset (&apply->changeset); } apply->mod_dir_id_of = rel_to_assoc (apply->changeset.mod_dirs_index, 0, 1); apply->new_dir_perms = rel_to_assoc (apply->changeset.added_dirs, 0, 2); /**************************************************************** * Inventory the target tree. */ mem_set0 ((t_uchar *)&inventory, sizeof (inventory)); arch_changeset_inventory (&inventory, apply->target, method, untagged_source_category, escape_classes); mem_set0 ((t_uchar *)&inventory_by_name, sizeof (inventory_by_name)); inventory_by_name.dirs = rel_copy_table (inventory.dirs); rel_sort_table_by_field (0, inventory_by_name.dirs, 0); inventory_by_name.files = rel_copy_table (inventory.files); rel_sort_table_by_field (0, inventory_by_name.files, 0); setup_running (apply, &inventory); /**************************************************************** * Set aside and delete removed files. */ identify_removed_and_missing_removed_files (&apply->changeset, &inventory, apply->report); /* FIXME: apply->report->missing_removed_files are conflicts and should be marked as such */ tmp_removed_files_root = rename_removed_files(talloc_context, apply->target, apply, &apply->running); /**************************************************************** * Set aside renamed files. */ renamed_files_index = rel_copy_table (apply->changeset.renamed_files); rel_sort_table_by_field (0, renamed_files_index, 2); present_renamed_files_index = rel_join (-1, rel_join_output (2,0, 1,0, 1,1, 2,1, -1), 2, 1, renamed_files_index, inventory.files); apply->report->missing_renamed_files = rel_join (1, rel_join_output (1,0, 1,1, 1,2, -1), 2, 1, renamed_files_index, inventory.files); for (x = 0; x < rel_n_records (apply->report->missing_renamed_files); ++x) two_path_callback (apply, "?r %s\n => %s\n", apply->report->missing_renamed_files[x][0], apply->report->missing_renamed_files[x][1]); rel_sort_table_by_field (0, apply->report->missing_removed_files, 0); tmp_renamed_files_root = talloc_tmp_file_name (tmp_removed_files_root, apply->target->root, ",,tmp-renamed-files"); rmrf_file (tmp_renamed_files_root); safe_mkdir (tmp_renamed_files_root, 0777); for (x = 0; x < rel_n_records (present_renamed_files_index); ++x) { t_uchar * target_loc = 0; t_uchar * target_id = 0; t_uchar * target_path = 0; t_uchar * dest_path = 0; t_uchar * dest_dir = 0; target_loc = present_renamed_files_index[x][0]; target_id = present_renamed_files_index[x][3]; target_path = file_name_in_vicinity (0, apply->target->root, target_loc); dest_path = file_name_in_vicinity (0, tmp_renamed_files_root, target_loc); dest_dir = file_name_directory_file (0, dest_path); ensure_directory_exists (dest_dir); safe_rename (target_path, dest_path); assoc_del (apply->running.file_id_of, target_loc); assoc_del (apply->running.file_loc_of, target_id); lim_free (0, target_path); lim_free (0, dest_path); lim_free (0, dest_dir); } /**************************************************************** * Set Aside Renamed and Removed Directories */ renamed_dirs_index = rel_copy_table (apply->changeset.renamed_dirs); rel_sort_table_by_field (0, renamed_dirs_index, 2); present_renamed_dirs_index = rel_join (-1, rel_join_output (2,0, 1,0, 1,1, 1,2, -1), 2, 1, renamed_dirs_index, inventory.dirs); rel_sort_table_by_field (1, present_renamed_dirs_index, 0); apply->report->missing_renamed_dirs = rel_join (1, rel_join_output (1,0, 1,1, 1,2, -1), 2, 1, renamed_dirs_index, inventory.dirs); rel_sort_table_by_field (0, apply->report->missing_renamed_dirs, 0); for (x = 0; x < rel_n_records (apply->report->missing_renamed_dirs); ++x) two_path_callback (apply, "?r/ %s\n => %s\n", apply->report->missing_renamed_dirs[x][0], apply->report->missing_renamed_dirs[x][1]); removed_dirs_index = rel_copy_table (apply->changeset.removed_dirs); rel_sort_table_by_field (0, removed_dirs_index, 1); present_removed_dirs_index = rel_join (-1, rel_join_output (2,0, 2,1, -1), 1, 1, removed_dirs_index, inventory.dirs); rel_sort_table_by_field (1, present_removed_dirs_index, 0); apply->report->missing_removed_dirs = rel_join (1, rel_join_output (1,0, 1,1, -1), 1, 1, removed_dirs_index, inventory.dirs); rel_sort_table_by_field (0, apply->report->missing_removed_dirs, 0); tmp_shuffled_dirs_root = talloc_tmp_file_name (tmp_removed_files_root, apply->target->root, ",,tmp-shuffled-dirs"); rmrf_file (tmp_shuffled_dirs_root); safe_mkdir (tmp_shuffled_dirs_root, 0777); /* It's important to set aside shuffled dirs from deepest * to shallowest. */ { int seq; int ren_pos; int rem_pos; seq = 0; ren_pos = 0; rem_pos = 0; while (1) { int ren_done; int rem_done; int ren_first; t_uchar * target_loc; t_uchar * id; rel_record * dest_record; t_uchar * tmp_name; ren_done = (ren_pos >= rel_n_records (present_renamed_dirs_index)); rem_done = (rem_pos >= rel_n_records (present_removed_dirs_index)); if (ren_done && rem_done) break; ren_first = (rem_done || (!ren_done && (0 < str_cmp (present_renamed_dirs_index[ren_pos][0], present_removed_dirs_index[rem_pos][0])))); if (ren_first) { target_loc = present_renamed_dirs_index[ren_pos][0]; id = present_renamed_dirs_index[ren_pos][3]; dest_record = &present_renamed_dirs_index[ren_pos]; ++ren_pos; } else { target_loc = present_removed_dirs_index[rem_pos][0]; id = present_removed_dirs_index[rem_pos][1]; dest_record = &present_removed_dirs_index[rem_pos]; ++rem_pos; one_path_callback (apply, "D/ %s\n", target_loc); rel_add_records (&apply->report->removed_dirs, rel_make_record (target_loc, id, 0), 0); } tmp_name = set_aside_shuffled_dirs (&file_set_aside_with_dir_id, &dir_set_aside_with_dir_id, target_loc, id, seq, tmp_shuffled_dirs_root, &apply->running, apply->target->root, &inventory); ++seq; rel_add_field (dest_record, tmp_name); lim_free (0, tmp_name); } rel_sort_table_by_field (0, apply->report->removed_dirs, 0); rel_sort_table_by_field (0, dir_set_aside_with_dir_id, 0); rel_sort_table_by_field (0, file_set_aside_with_dir_id, 0); } /**************************************************************** * Make a name for a dir in which to stash removed .rej and .orig files */ apply->removed_patch_conflict_files_path = talloc_tmp_file_name (apply, apply->target->root, "+removed-conflict-files"); /**************************************************************** * What we have: * * We have the target tree, with all renamed/removed dirs and files * set aside. * * The removed files are all under $tmp_removed_files_root/$tgtloc. * * The renamed files are all under $tmp_renamed_files_root/$tgtloc. * * (in both of the above cases, $tgtloc is the original target loc) * * Both the renamed and removed directories are set aside * in $tmp_shuffled_dirs_root under integer filenames. * The tmp_name fields of present_renamed_dirs_index and * present_renamed_dirs_index are full paths to these temp * names. * * Todo: * * We have to install renamed and new directories, from shallowest to * deepest, then install renamed and new files (in any order). * * Each installed item has a destination path that has, in essense, * three parts: * * $tgthas / $newrelpath / $basename * * where $tgthas is the deepest part of the destination path that * the target tree already has when the item is installed, * $newrelpath are additional intermediate directories that need to * be created (possibly causing conflicts) and $basename is the final * name for the item (possibly causing a conflict). * * That three part path is derived from the $modloc of the item * by finding the deepest containing directory in $modloc which * is present (by id) in $target (that's $tgthas). * * In the code that follows: * * tgthas == $tgthas * install_dir == $tgthas / $newrelpath * install_name == $tgthas / $newrelpath / $basename * * Finally, then, we have to apply individual file and dir patches. */ /**************************************************************** * Compute an install plan for new and renamed directories. * * We have to add or rename containing dirs before contained. * * So, we need a plan for that. */ install_dirs_plan = rel_cut (rel_cut_list (2, 4, 3, 0, -1), present_renamed_dirs_index); for (x = 0; x < rel_n_records (apply->changeset.added_dirs); ++x) { rel_add_records (&install_dirs_plan, rel_make_record (apply->changeset.added_dirs[x][0], "", apply->changeset.added_dirs[x][1], 0), 0); } rel_sort_table_by_field_fn (0, install_dirs_plan, 0, dir_depth_cmp); /**************************************************************** * Install dirs. */ for (x = 0; x < rel_n_records (install_dirs_plan); ++x) { t_uchar * mod_loc; t_uchar * take_from; t_uchar * id; t_uchar * target_has_dir = 0; t_uchar * target_has_path = 0; t_uchar * install_dir = 0; t_uchar * install_name = 0; t_uchar * install_loc = 0; mod_loc = install_dirs_plan[x][0]; take_from = install_dirs_plan[x][1]; id = install_dirs_plan[x][2]; if (!*take_from) take_from = 0; /* FIXME: trigger a conflict here on non-zero */ analyze_install (&apply->running, 1, 0, &target_has_dir, &install_dir, &install_name, &install_loc, apply->target->root, mod_loc, id, apply->mod_dir_id_of, file_set_aside_with_dir_id, dir_set_aside_with_dir_id); if (!str_cmp (target_has_dir, ".")) target_has_path = str_save (0, apply->target->root); else target_has_path = file_name_in_vicinity (0, apply->target->root, 2 + target_has_dir); /* over "./" */ ensure_directory_eliminating_conflicts (apply, target_has_path, install_dir); if (!take_from) { if (!safe_file_is_directory (install_name)) { int trust_umask = 0; int ign; t_uchar * perms_str; unsigned long perms; perms_str = assoc_ref (apply->new_dir_perms, mod_loc); if (cvt_octal_to_ulong (&ign, &perms, perms_str, str_length (perms_str))) { perms = 0777; trust_umask = 1; } if (!deferred_conflict (apply, install_loc, 0, &apply->running)) { one_path_callback (apply, "A/ %s\n", install_loc); rel_add_records (&apply->report->new_dirs, rel_make_record (install_loc, id, 0), 0); } else { one_path_callback (apply, "CA/ %s\n", install_loc); rel_add_records (&apply->report->conflict_dirs, rel_make_record (install_loc, id, 0), 0); } safe_mkdir (install_name, perms); if (!trust_umask) safe_chmod (install_name, perms); } } else { t_uchar * oldtgtloc; oldtgtloc = install_dirs_plan[x][3]; if (!deferred_conflict (apply, install_loc, 0, &apply->running)) { two_path_callback (apply, "/> %s\t%s\n", oldtgtloc, install_loc); rel_add_records (&apply->report->renamed_dirs, rel_make_record (oldtgtloc, install_loc, id, 0), 0); } else { two_path_callback (apply, "C/> %s\t%s\n", oldtgtloc, install_loc); rel_add_records (&apply->report->conflict_dirs, rel_make_record (install_loc, id, 0), 0); } safe_rename (take_from, install_name); } lim_free (0, target_has_dir); lim_free (0, target_has_path); lim_free (0, install_dir); lim_free (0, install_name); lim_free (0, install_loc); } rel_sort_table_by_field (0, apply->report->new_dirs, 0); rel_sort_table_by_field (0, apply->report->renamed_dirs, 0); /**************************************************************** * Install Renamed Files and Symlinks */ for (x = 0; x < rel_n_records (present_renamed_files_index); ++x) { t_uchar * old_target_loc; t_uchar * mod_loc; t_uchar * id; t_uchar * take_from = 0; t_uchar * target_has_dir = 0; t_uchar * install_dir = 0; t_uchar * install_name = 0; t_uchar * install_loc = 0; t_uchar * target_has_path = 0; old_target_loc = present_renamed_files_index[x][0]; mod_loc = present_renamed_files_index[x][2]; id = present_renamed_files_index[x][3]; take_from = file_name_in_vicinity (0, tmp_renamed_files_root, old_target_loc); /* renames cannot conflict on changed-paths */ invariant (!analyze_install (&apply->running, 0, 1, &target_has_dir, &install_dir, &install_name, &install_loc, apply->target->root, mod_loc, id, apply->mod_dir_id_of, 0, 0)); if (!str_cmp (target_has_dir, ".")) target_has_path = str_save (0, apply->target->root); else target_has_path = file_name_in_vicinity (0, apply->target->root, 2 + target_has_dir); /* over "./" */ ensure_directory_eliminating_conflicts (apply, target_has_path, install_dir); if (!deferred_conflict (apply, install_loc, 0, &apply->running)) { two_path_callback (apply, "=> %s\t%s\n", old_target_loc, install_loc); rel_add_records (&apply->report->renamed_files, rel_make_record (old_target_loc, install_loc, id, 0), 0); } else { two_path_callback (apply, "C=> %s\t%s\n", old_target_loc, install_loc); rel_add_records (&apply->report->conflict_files, rel_make_record (install_loc, id, 0), 0); } safe_rename (take_from, install_name); lim_free (0, take_from); lim_free (0, target_has_dir); lim_free (0, install_dir); lim_free (0, install_name); lim_free (0, install_loc); lim_free (0, target_has_path); } added_files_and_symlinks = rel_copy_table (apply->changeset.added_files); rel_append_x (&added_files_and_symlinks, apply->changeset.added_symlinks); for (x = 0; x < rel_n_records (added_files_and_symlinks); ++x) { t_uchar * mod_loc; t_uchar * id; t_uchar * take_from; int path_conflict; t_uchar * target_has_dir = 0; t_uchar * install_dir = 0; t_uchar * install_name = 0; t_uchar * install_loc = 0; t_uchar * target_has_path = 0; mod_loc = added_files_and_symlinks[x][0]; id = added_files_and_symlinks[x][1]; take_from = added_files_and_symlinks[x][2]; path_conflict = analyze_install (&apply->running, 0, 0, &target_has_dir, &install_dir, &install_name, &install_loc, apply->target->root, mod_loc, id, apply->mod_dir_id_of, 0, 0); if (!str_cmp (target_has_dir, ".")) target_has_path = str_save (0, apply->target->root); else target_has_path = file_name_in_vicinity (0, apply->target->root, 2 + target_has_dir); /* over "./" */ ensure_directory_eliminating_conflicts (apply, target_has_path, install_dir); if (path_conflict) { one_path_callback (apply, "CA %s (id present already)\n", install_loc); rel_add_records (&apply->report->conflict_files, rel_make_record (install_loc, id, 0), 0); rel_add_records (&apply->report->conflict_files, rel_make_record (assoc_ref (apply->running.file_loc_of, id), id, 0), 0); } else if (!deferred_conflict (apply, install_loc, take_from, &apply->running)) { one_path_callback (apply, "A %s\n", install_loc); rel_add_records (&apply->report->new_files, rel_make_record (install_loc, id, 0), 0); } else { one_path_callback (apply, "CA %s\n", install_loc); rel_add_records (&apply->report->conflict_files, rel_make_record (install_loc, id, 0), 0); } copy_file_or_symlink (take_from, install_name); copy_permissions (take_from, install_name); lim_free (0, target_has_dir); lim_free (0, install_dir); lim_free (0, install_name); lim_free (0, install_loc); lim_free (0, target_has_path); } rel_sort_table_by_field (0, apply->report->renamed_files, 0); /**************************************************************** * Patch Regular Files */ for (x = 0; x < rel_n_records (apply->changeset.patched_regular_files); ++x) { t_uchar * mod_loc; t_uchar * id; t_uchar * target_loc; t_uchar * patch_path = 0; t_uchar * target_path = 0; struct stat target_stat; patch_path = str_alloc_cat (0, apply->changeset.patched_regular_files[x][2], ".patch"); mod_loc = apply->changeset.patched_regular_files[x][0]; id = apply->changeset.patched_regular_files[x][1]; target_loc = assoc_ref (apply->running.file_loc_of, id); if (!target_loc) { one_path_callback (apply, "?M %s\n", mod_loc); rel_add_records (&apply->report->missing_file_for_patch, rel_make_record (mod_loc, id, 0), 0); save_patch_for_missing_file (missing_patch_dir, patch_path, mod_loc); continue; } target_path = file_name_in_vicinity (0, apply->target->root, target_loc); safe_lstat (target_path, &target_stat); preserve_old_patch_spew (apply->removed_patch_conflict_files_path, apply->target, target_loc, &apply->running); if (S_ISLNK (target_stat.st_mode)) { t_uchar * orig_name; t_uchar * rej_name; conflict_filenames (apply->target->root, target_path, &orig_name, &rej_name, &apply->running); safe_rename (target_path, orig_name); copy_file (patch_path, rej_name); copy_permissions (patch_path, rej_name); one_path_callback (apply, "C-> %s\n", target_loc); rel_add_records (&apply->report->conflict_files, rel_make_record (target_loc, id, 0), 0); } else if (arch_id_indicates_changelog (id)) { rel_add_records (&patched_changelogs, rel_make_record (target_loc, id, target_path, 0), 0); } else { struct stat patch_file_stat; safe_stat (patch_path, &patch_file_stat); if (patch_file_stat.st_size) { int errn; t_uchar * target_dir = 0; t_uchar * basename = 0; t_uchar * original_inode_basename = 0; t_uchar * original_inode_tmpname = 0; int patch_stat = -1; target_dir = file_name_directory_file (0, target_path); basename = file_name_tail (0, target_path); original_inode_basename = str_alloc_cat_many (0, ",,dopatch.", basename, ".", str_end); safe_chdir (target_dir); original_inode_tmpname = talloc_tmp_file_name (talloc_context, ".", original_inode_basename); safe_rename (basename, original_inode_tmpname); if (vu_unlink (&errn, ",,patch-output") && (errn != ENOENT)) { safe_printfmt (2, "arch_apply_changeset: unable to unlink file\n"); safe_printfmt (2, " file: %s/,,patch-output\n", target_dir); safe_printfmt (2, " error: %s\n", errno_to_string (errn)); exit (2); } patch_stat = run_patch (reverse, forward, patch_path, basename, original_inode_tmpname); if (patch_stat == 0) { copy_permissions (original_inode_tmpname, basename); safe_unlink (original_inode_tmpname); one_path_callback (apply, "M %s\n", target_loc); rel_add_records (&apply->report->modified_files, rel_make_record (target_loc, id, 0), 0); } else if (patch_stat == 1) { t_uchar * orig_name; copy_permissions (original_inode_tmpname, basename); conflict_filenames (apply->target->root, target_path, &orig_name, NULL, &apply->running); safe_rename (original_inode_tmpname, orig_name); one_path_callback (apply, "C %s\n", target_loc); rel_add_records (&apply->report->conflict_files, rel_make_record (target_loc, id, 0), 0); lim_free (0, orig_name); } else { int in_fd; safe_printfmt (2, "arch_apply_changeset: internal error (patch returned odd status)\n"); safe_printfmt (2, " patch exit status: %d\n", patch_stat); safe_printfmt (2, " target file: %s\n", target_path); safe_printfmt (2, "\n"); in_fd = safe_open (",,patch-output", O_RDONLY, 0); copy_fd (in_fd, 2); safe_printfmt (2, "\n"); exit (2); } if (vu_unlink (&errn, ",,patch-output") && (errn != ENOENT)) { safe_printfmt (2, "arch_apply_changeset: unable to unlink file\n"); safe_printfmt (2, " file: %s/,,patch-output\n", target_dir); safe_printfmt (2, " error: %s\n", errno_to_string (errn)); exit (2); } safe_fchdir (apply->here_fd); lim_free (0, target_dir); lim_free (0, basename); lim_free (0, original_inode_basename); talloc_free (original_inode_tmpname); } } lim_free (0, patch_path); lim_free (0, target_path); } /**************************************************************** * Patch Symlinks */ for (x = 0; x < rel_n_records (apply->changeset.patched_symlinks); ++x) { t_uchar * mod_loc; t_uchar * id; t_uchar * target_loc; t_uchar * orig_patch_path = 0; t_uchar * mod_patch_path = 0; t_uchar * target_path = 0; struct stat target_stat; if (!reverse) { orig_patch_path = str_alloc_cat (0, apply->changeset.patched_symlinks[x][2], ".link-orig"); mod_patch_path = str_alloc_cat (0, apply->changeset.patched_symlinks[x][2], ".link-mod"); } else { orig_patch_path = str_alloc_cat (0, apply->changeset.patched_symlinks[x][2], ".link-mod"); mod_patch_path = str_alloc_cat (0, apply->changeset.patched_symlinks[x][2], ".link-orig"); } mod_loc = apply->changeset.patched_symlinks[x][0]; id = apply->changeset.patched_symlinks[x][1]; target_loc = assoc_ref (apply->running.file_loc_of, id); if (!target_loc) { one_path_callback (apply, "?M %s\n", mod_loc); rel_add_records (&apply->report->missing_file_for_patch, rel_make_record (mod_loc, id, 0), 0); save_patch_for_missing_file (missing_patch_dir, orig_patch_path, mod_loc); save_patch_for_missing_file (missing_patch_dir, mod_patch_path, mod_loc); continue; } target_path = file_name_in_vicinity (0, apply->target->root, target_loc); safe_lstat (target_path, &target_stat); preserve_old_patch_spew (apply->removed_patch_conflict_files_path, apply->target, target_loc, &apply->running); if (!S_ISLNK (target_stat.st_mode)) { t_uchar * orig_name = 0; t_uchar * rej_name = 0; int out_fd; int in_fd; symlink_conflict: conflict_filenames (apply->target->root, target_path, &orig_name, &rej_name, &apply->running); safe_rename (target_path, orig_name); out_fd = safe_open (rej_name, O_WRONLY | O_CREAT | O_EXCL, 0666); safe_printfmt (out_fd, "Patched wanted to retarget a symbolic link:\n\n"); safe_printfmt (out_fd, " from: "); in_fd = safe_open (orig_patch_path, O_RDONLY, 0); copy_fd (in_fd, out_fd); safe_close (in_fd); safe_printfmt (out_fd, "\n to: "); in_fd = safe_open (mod_patch_path, O_RDONLY, 0); copy_fd (in_fd, out_fd); safe_close (in_fd); safe_printfmt (out_fd, "\n"); one_path_callback (apply, "C-> %s\n", target_loc); rel_add_records (&apply->report->conflict_files, rel_make_record (target_loc, id, 0), 0); lim_free (0, orig_name); lim_free (0, rej_name); } else { /**************************************************************** * FIX THIS * * Strictly speaking: * * We should really try to "remap" the orig and mod link targets for * the current tree layout. */ t_uchar * orig_link_target = 0; t_uchar * mod_link_target = 0; t_uchar * tree_link_target = 0; int patch_stat; orig_link_target = read_line_from_file (orig_patch_path); mod_link_target = read_line_from_file (mod_patch_path); tree_link_target = link_target (target_path); if (!str_cmp (mod_link_target, tree_link_target)) { patch_stat = 0; } else if (!str_cmp (orig_link_target, tree_link_target)) { t_uchar * target_path_dir = 0; t_uchar * target_path_tail = 0; t_uchar * tmp_file_basename = 0; t_uchar * tmp_file_path = 0; target_path_dir = file_name_directory_file (0, target_path); target_path_tail = file_name_tail (0, target_path); tmp_file_basename = str_alloc_cat_many (0, ",,dopatch.", target_path_tail, ".", str_end); tmp_file_path = talloc_tmp_file_name (talloc_context, target_path_dir, tmp_file_basename); rmrf_file (tmp_file_path); safe_rename (target_path, tmp_file_path); safe_symlink (mod_link_target, target_path); safe_unlink (tmp_file_path); patch_stat = 0; lim_free (0, target_path_dir); lim_free (0, target_path_tail); lim_free (0, tmp_file_basename); talloc_free (tmp_file_path); } else { patch_stat = 1; } if (patch_stat == 0) { one_path_callback (apply, "M-> %s\n", target_loc); rel_add_records (&apply->report->modified_files, rel_make_record (target_loc, id, 0), 0); } else goto symlink_conflict; lim_free (0, orig_link_target); lim_free (0, mod_link_target); lim_free (0, tree_link_target); } lim_free (0, orig_patch_path); lim_free (0, mod_patch_path); lim_free (0, target_path); } /**************************************************************** * Patch Binaries */ for (x = 0; x < rel_n_records (apply->changeset.patched_binaries); ++x) { t_uchar * mod_loc; t_uchar * id; t_uchar * target_loc; t_uchar * orig_patch_path = 0; t_uchar * mod_patch_path = 0; t_uchar * target_path = 0; struct stat target_stat; if (!reverse) { orig_patch_path = str_alloc_cat (0, apply->changeset.patched_binaries[x][2], ".original"); mod_patch_path = str_alloc_cat (0, apply->changeset.patched_binaries[x][2], ".modified"); } else { orig_patch_path = str_alloc_cat (0, apply->changeset.patched_binaries[x][2], ".modified"); mod_patch_path = str_alloc_cat (0, apply->changeset.patched_binaries[x][2], ".original"); } mod_loc = apply->changeset.patched_binaries[x][0]; id = apply->changeset.patched_binaries[x][1]; target_loc = assoc_ref (apply->running.file_loc_of, id); if (!target_loc) { one_path_callback (apply, "?M %s\n", mod_loc); rel_add_records (&apply->report->missing_file_for_patch, rel_make_record (mod_loc, id, 0), 0); save_patch_for_missing_file (missing_patch_dir, orig_patch_path, mod_loc); save_patch_for_missing_file (missing_patch_dir, mod_patch_path, mod_loc); continue; } target_path = file_name_in_vicinity (0, apply->target->root, target_loc); safe_lstat (target_path, &target_stat); preserve_old_patch_spew (apply->removed_patch_conflict_files_path, apply->target, target_loc, &apply->running); if (S_ISLNK (target_stat.st_mode)) { t_uchar * orig_name; t_uchar * rej_name; binary_conflict: conflict_filenames (apply->target->root, target_path, &orig_name, &rej_name, &apply->running); safe_rename (target_path, orig_name); copy_file (mod_patch_path, rej_name); copy_permissions (mod_patch_path, rej_name); one_path_callback (apply, "Cb %s\n", target_loc); rel_add_records (&apply->report->conflict_files, rel_make_record (target_loc, id, 0), 0); lim_free (0, orig_name); lim_free (0, rej_name); } else { int patch_stat; if (arch_binary_files_differ (orig_patch_path, target_path, 0, 0)) { patch_stat = 1; } else { t_uchar * target_path_dir = 0; t_uchar * target_path_tail = 0; t_uchar * tmp_file_basename = 0; t_uchar * tmp_file_path = 0; target_path_dir = file_name_directory_file (0, target_path); target_path_tail = file_name_tail (0, target_path); tmp_file_basename = str_alloc_cat_many (0, ",,dopatch.", target_path_tail, ".", str_end); tmp_file_path = talloc_tmp_file_name (talloc_context,target_path_dir, tmp_file_basename); rmrf_file (tmp_file_path); safe_rename (target_path, tmp_file_path); copy_file (mod_patch_path, target_path); copy_permissions (mod_patch_path, target_path); safe_unlink (tmp_file_path); patch_stat = 0; lim_free (0, target_path_dir); lim_free (0, target_path_tail); lim_free (0, tmp_file_basename); talloc_free (tmp_file_path); } if (patch_stat == 0) { one_path_callback (apply, "Mb %s\n", target_loc); rel_add_records (&apply->report->modified_files, rel_make_record (target_loc, id, 0), 0); } else goto binary_conflict; } lim_free (0, orig_patch_path); lim_free (0, mod_patch_path); lim_free (0, target_path); } /**************************************************************** * Symlinks->File Patches */ for (x = 0; x < rel_n_records (apply->changeset.symlink_to_file); ++x) { t_uchar * mod_loc; t_uchar * id; t_uchar * target_loc; t_uchar * orig_patch_path = 0; t_uchar * mod_patch_path = 0; t_uchar * target_path = 0; struct stat target_stat; if (!reverse) { orig_patch_path = str_alloc_cat (0, apply->changeset.symlink_to_file[x][2], ".link-orig"); mod_patch_path = str_alloc_cat (0, apply->changeset.symlink_to_file[x][2], ".modified"); } else { orig_patch_path = str_alloc_cat (0, apply->changeset.symlink_to_file[x][2], ".modified"); mod_patch_path = str_alloc_cat (0, apply->changeset.symlink_to_file[x][2], ".link-orig"); } mod_loc = apply->changeset.symlink_to_file[x][0]; id = apply->changeset.symlink_to_file[x][1]; target_loc = assoc_ref (apply->running.file_loc_of, id); if (!target_loc) { one_path_callback (apply, "?M %s\n", mod_loc); rel_add_records (&apply->report->missing_file_for_patch, rel_make_record (mod_loc, id, 0), 0); save_patch_for_missing_file (missing_patch_dir, orig_patch_path, mod_loc); save_patch_for_missing_file (missing_patch_dir, mod_patch_path, mod_loc); continue; } target_path = file_name_in_vicinity (0, apply->target->root, target_loc); safe_lstat (target_path, &target_stat); preserve_old_patch_spew (apply->removed_patch_conflict_files_path, apply->target, target_loc, &apply->running); if (!S_ISLNK (target_stat.st_mode)) { t_uchar * orig_name; t_uchar * rej_name; symlink_to_file_conflict: conflict_filenames (apply->target->root, target_path, &orig_name, &rej_name, &apply->running); safe_rename (target_path, orig_name); copy_file (mod_patch_path, rej_name); copy_permissions (mod_patch_path, rej_name); one_path_callback (apply, "Cch %s\n", target_loc); rel_add_records (&apply->report->conflict_files, rel_make_record (target_loc, id, 0), 0); lim_free (0, orig_name); lim_free (0, rej_name); } else { t_uchar * orig_link_target = 0; t_uchar * tree_link_target = 0; int patch_stat; orig_link_target = read_line_from_file (orig_patch_path); tree_link_target = link_target (target_path); if (str_cmp (orig_link_target, tree_link_target)) { patch_stat = 1; } else { t_uchar * target_path_dir = 0; t_uchar * target_path_tail = 0; t_uchar * tmp_file_basename = 0; t_uchar * tmp_file_path = 0; target_path_dir = file_name_directory_file (0, target_path); target_path_tail = file_name_tail (0, target_path); tmp_file_basename = str_alloc_cat_many (0, ",,dopatch.", target_path_tail, ".", str_end); tmp_file_path = talloc_tmp_file_name (talloc_context, target_path_dir, tmp_file_basename); rmrf_file (tmp_file_path); safe_rename (target_path, tmp_file_path); copy_file (mod_patch_path, target_path); copy_permissions (mod_patch_path, target_path); safe_unlink (tmp_file_path); patch_stat = 0; lim_free (0, target_path_dir); lim_free (0, target_path_tail); lim_free (0, tmp_file_basename); talloc_free (tmp_file_path); } if (patch_stat == 0) { one_path_callback (apply, "ch %s\n", target_loc); rel_add_records (&apply->report->modified_files, rel_make_record (target_loc, id, 0), 0); } else goto symlink_to_file_conflict; lim_free (0, orig_link_target); lim_free (0, tree_link_target); } lim_free (0, orig_patch_path); lim_free (0, mod_patch_path); lim_free (0, target_path); } /**************************************************************** * File->Symlink Patches */ for (x = 0; x < rel_n_records (apply->changeset.file_to_symlink); ++x) { t_uchar * mod_loc; t_uchar * id; t_uchar * target_loc; t_uchar * orig_patch_path = 0; t_uchar * mod_patch_path = 0; t_uchar * target_path = 0; struct stat target_stat; if (!reverse) { orig_patch_path = str_alloc_cat (0, apply->changeset.file_to_symlink[x][2], ".original"); mod_patch_path = str_alloc_cat (0, apply->changeset.file_to_symlink[x][2], ".link-mod"); } else { orig_patch_path = str_alloc_cat (0, apply->changeset.file_to_symlink[x][2], ".link-mod"); mod_patch_path = str_alloc_cat (0, apply->changeset.file_to_symlink[x][2], ".original"); } mod_loc = apply->changeset.file_to_symlink[x][0]; id = apply->changeset.file_to_symlink[x][1]; target_loc = assoc_ref (apply->running.file_loc_of, id); if (!target_loc) { one_path_callback (apply, "?M %s\n", mod_loc); rel_add_records (&apply->report->missing_file_for_patch, rel_make_record (mod_loc, id, 0), 0); save_patch_for_missing_file (missing_patch_dir, orig_patch_path, mod_loc); save_patch_for_missing_file (missing_patch_dir, mod_patch_path, mod_loc); continue; } target_path = file_name_in_vicinity (0, apply->target->root, target_loc); safe_lstat (target_path, &target_stat); preserve_old_patch_spew (apply->removed_patch_conflict_files_path, apply->target, target_loc, &apply->running); if (S_ISLNK (target_stat.st_mode)) { t_uchar * orig_name; t_uchar * rej_name; int out_fd; int in_fd; file_to_symlink_conflict: conflict_filenames (apply->target->root, target_path, &orig_name, &rej_name, &apply->running); safe_rename (target_path, orig_name); out_fd = safe_open (rej_name, O_WRONLY | O_CREAT | O_EXCL, 0666); safe_printfmt (out_fd, "MOD had a symlink to: "); in_fd = safe_open (mod_patch_path, O_RDONLY, 0); copy_fd (in_fd, out_fd); safe_close (in_fd); safe_close (out_fd); one_path_callback (apply, "Cch %s\n", target_loc); rel_add_records (&apply->report->conflict_files, rel_make_record (target_loc, id, 0), 0); lim_free (0, orig_name); lim_free (0, rej_name); } else { t_uchar * mod_link_target = 0; t_uchar * tree_link_target = 0; int patch_stat; mod_link_target = read_line_from_file (mod_patch_path); if (arch_binary_files_differ (orig_patch_path, target_path, 0, 0)) { patch_stat = 1; } else { t_uchar * target_path_dir = 0; t_uchar * target_path_tail = 0; t_uchar * tmp_file_basename = 0; t_uchar * tmp_file_path = 0; target_path_dir = file_name_directory_file (0, target_path); target_path_tail = file_name_tail (0, target_path); tmp_file_basename = str_alloc_cat_many (0, ",,dopatch.", target_path_tail, ".", str_end); tmp_file_path = talloc_tmp_file_name (talloc_context, target_path_dir, tmp_file_basename); rmrf_file (tmp_file_path); safe_rename (target_path, tmp_file_path); safe_symlink (mod_link_target, target_path); safe_unlink (tmp_file_path); patch_stat = 0; lim_free (0, target_path_dir); lim_free (0, target_path_tail); lim_free (0, tmp_file_basename); talloc_free (tmp_file_path); } if (patch_stat == 0) { one_path_callback (apply, "ch %s\n", target_loc); rel_add_records (&apply->report->modified_files, rel_make_record (target_loc, id, 0), 0); } else goto file_to_symlink_conflict; lim_free (0, mod_link_target); lim_free (0, tree_link_target); } lim_free (0, orig_patch_path); lim_free (0, mod_patch_path); lim_free (0, target_path); } /**************************************************************** * Patch File Metadata */ for (x = 0; x < rel_n_records (apply->changeset.file_metadata_changed); ++x) { t_uchar * mod_loc; t_uchar * id; t_uchar * target_loc; t_uchar * orig_patch_path = 0; t_uchar * mod_patch_path = 0; t_uchar * target_path = 0; t_uchar * target_spew_loc = 0; t_uchar * target_spew_path = 0; struct stat target_stat; if (!reverse) { orig_patch_path = str_alloc_cat (0, apply->changeset.file_metadata_changed[x][2], ".meta-orig"); mod_patch_path = str_alloc_cat (0, apply->changeset.file_metadata_changed[x][2], ".meta-mod"); } else { orig_patch_path = str_alloc_cat (0, apply->changeset.file_metadata_changed[x][2], ".meta-mod"); mod_patch_path = str_alloc_cat (0, apply->changeset.file_metadata_changed[x][2], ".meta-orig"); } mod_loc = apply->changeset.file_metadata_changed[x][0]; id = apply->changeset.file_metadata_changed[x][1]; target_loc = assoc_ref (apply->running.file_loc_of, id); if (!target_loc) { one_path_callback (apply, "?-- %s\n", mod_loc); rel_add_records (&apply->report->missing_file_for_meta_patch, rel_make_record (mod_loc, id, 0), 0); save_patch_for_missing_file (missing_patch_dir, orig_patch_path, mod_loc); save_patch_for_missing_file (missing_patch_dir, mod_patch_path, mod_loc); continue; } target_path = file_name_in_vicinity (0, apply->target->root, target_loc); target_spew_loc = str_alloc_cat (0, target_loc, ".meta"); target_spew_path = file_name_in_vicinity (0, apply->target->root, target_spew_loc); preserve_old_patch_spew (apply->removed_patch_conflict_files_path, apply->target, target_spew_loc, &apply->running); safe_lstat (target_path, &target_stat); if (S_ISLNK (target_stat.st_mode)) { t_uchar * rej_name; int out_fd; int in_fd; rej_name = str_alloc_cat (0, target_spew_path, ".rej"); out_fd = safe_open (rej_name, O_WRONLY | O_CREAT | O_EXCL, 0666); safe_printfmt (out_fd, "MOD had a metadata change: "); in_fd = safe_open (mod_patch_path, O_RDONLY, 0); copy_fd (in_fd, out_fd); safe_close (in_fd); safe_close (out_fd); one_path_callback (apply, "C-- %s\n", target_loc); rel_add_records (&apply->report->metadata_conflict_files, rel_make_record (target_loc, id, 0), 0); lim_free (0, rej_name); } else { mode_t orig_perms; mode_t mod_perms; mod_perms = arch_read_permissions_patch (mod_patch_path); orig_perms = arch_read_permissions_patch (orig_patch_path); if (mod_perms != (target_stat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO))) { t_uchar * target_path_dir = 0; t_uchar * target_path_tmp = 0; target_path_dir = file_name_directory_file (0, target_path); target_path_tmp = talloc_tmp_file_name (talloc_context, target_path_dir, ",,meta-tmp"); rmrf_file (target_path_tmp); copy_file (target_path, target_path_tmp); safe_chmod (target_path_tmp, mod_perms); safe_rename (target_path_tmp, target_path); lim_free (0, target_path_dir); talloc_free (target_path_tmp); } one_path_callback (apply, "-- %s\n", target_loc); rel_add_records (&apply->report->meta_modified_files, rel_make_record (target_loc, id, 0), 0); } lim_free (0, orig_patch_path); lim_free (0, mod_patch_path); lim_free (0, target_path); lim_free (0, target_spew_loc); lim_free (0, target_spew_path); } /**************************************************************** * Patch Dir Metadata */ for (x = 0; x < rel_n_records (apply->changeset.dir_metadata_changed); ++x) { t_uchar * mod_loc; t_uchar * id; t_uchar * target_loc; t_uchar * orig_patch_path = 0; t_uchar * mod_patch_path = 0; t_uchar * target_path = 0; t_uchar * target_spew_loc = 0; t_uchar * target_spew_path = 0; struct stat target_stat; if (!reverse) { orig_patch_path = file_name_in_vicinity (0, apply->changeset.dir_metadata_changed[x][2], "=dir-meta-orig"); mod_patch_path = file_name_in_vicinity (0, apply->changeset.dir_metadata_changed[x][2], "=dir-meta-mod"); } else { orig_patch_path = file_name_in_vicinity (0, apply->changeset.dir_metadata_changed[x][2], "=dir-meta-mod"); mod_patch_path = file_name_in_vicinity (0, apply->changeset.dir_metadata_changed[x][2], "=dir-meta-orig"); } mod_loc = apply->changeset.dir_metadata_changed[x][0]; id = apply->changeset.dir_metadata_changed[x][1]; target_loc = assoc_ref (apply->running.dir_loc_of, id); if (!target_loc) { one_path_callback (apply, "?-/ %s\n", mod_loc); rel_add_records (&apply->report->missing_dir_for_meta_patch, rel_make_record (mod_loc, id, 0), 0); save_patch_for_missing_file (missing_patch_dir, orig_patch_path, mod_loc); save_patch_for_missing_file (missing_patch_dir, mod_patch_path, mod_loc); continue; } target_path = file_name_in_vicinity (0, apply->target->root, target_loc); target_spew_loc = str_alloc_cat (0, target_loc, ".meta"); target_spew_path = file_name_in_vicinity (0, apply->target->root, target_spew_loc); preserve_old_patch_spew (apply->removed_patch_conflict_files_path, apply->target, target_spew_loc, &apply->running); safe_stat (target_path, &target_stat); { mode_t orig_perms; mode_t mod_perms; mod_perms = arch_read_permissions_patch (mod_patch_path); orig_perms = arch_read_permissions_patch (orig_patch_path); if (mod_perms != (target_stat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO))) { safe_chmod (target_path, mod_perms); } one_path_callback (apply, "--/ %s\n", target_loc); rel_add_records (&apply->report->meta_modified_dirs, rel_make_record (target_loc, id, 0), 0); } lim_free (0, orig_patch_path); lim_free (0, mod_patch_path); lim_free (0, target_path); lim_free (0, target_spew_loc); lim_free (0, target_spew_path); } /**************************************************************** * Update Changelogs */ for (x = 0; x < rel_n_records (patched_changelogs); ++x) { t_uchar * target_loc; t_uchar * id; t_uchar * target_path; t_uchar * target_dir = 0; t_uchar * target_tmp = 0; t_uchar * archive = 0; t_uchar * version = 0; int out_fd; struct stat stat_was; mode_t mode; target_loc = patched_changelogs[x][0]; id = patched_changelogs[x][1]; target_path = patched_changelogs[x][2]; target_dir = file_name_directory_file (0, target_path); target_tmp = talloc_tmp_file_name (talloc_context, target_dir, ",,new-changeset"); safe_stat (target_path, &stat_was); mode = (stat_was.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)); arch_parse_changelog_id (&archive, &version, id); rmrf_file (target_tmp); out_fd = safe_open (target_tmp, O_WRONLY | O_EXCL | O_CREAT, mode); safe_fchmod (out_fd, mode); safe_buffer_fd (out_fd, 0, O_WRONLY, 0); arch_generate_changelog (out_fd, apply->target, 0, 0, 0, 0, archive, version); safe_close (out_fd); safe_rename (target_tmp, target_path); one_path_callback (apply, "cl %s\n", target_loc); rel_add_records (&apply->report->modified_files, rel_make_record (target_loc, id, 0), 0); lim_free (0, target_dir); talloc_free (target_tmp); lim_free (0, archive); lim_free (0, version); } /**************************************************************** * Finish Up Deferred Conflicts */ rel_sort_table_by_field (1, apply->deferred_conflicts, 0); for (x = 0; x < rel_n_records (apply->deferred_conflicts); ++x) { t_uchar * target_path = 0; t_uchar * rej_path = 0; target_path = file_name_in_vicinity (0, apply->target->root, apply->deferred_conflicts[x][0]); rej_path = str_alloc_cat (0, target_path, ".rej"); safe_rename (target_path, rej_path); lim_free (0, target_path); lim_free (0, rej_path); } report_sort_and_unique (apply->report); /* FIXME: here is where we note conflicts */ arch_tree_note_conflicts (apply->target, apply->report); arch_project_tree_mutated (apply->target); /**************************************************************** * cleanup */ lim_free (0, missing_patch_dir); arch_free_changeset_inventory_data (&inventory); arch_free_changeset_inventory_data (&inventory_by_name); rmrf_file (tmp_removed_files_root); rmrf_file (tmp_renamed_files_root); rmrf_file (tmp_shuffled_dirs_root); talloc_free (tmp_removed_files_root); rel_free_table (renamed_files_index); rel_free_table (present_renamed_files_index); rel_free_table (renamed_dirs_index); rel_free_table (present_renamed_dirs_index); rel_free_table (removed_dirs_index); rel_free_table (present_removed_dirs_index); rel_free_table (file_set_aside_with_dir_id); rel_free_table (dir_set_aside_with_dir_id); rel_free_table (install_dirs_plan); rel_free_table (added_files_and_symlinks); rel_free_table (patched_changelogs); result = talloc_steal (context, apply->report); talloc_free (apply); return result; } /** * \brief create a new changeset application object */ apply_changeset_t * arch_apply_changeset_new (void * context, arch_apply_changeset_report_callback callback, void * thunk, int escape_classes, arch_project_tree_t * target) { apply_changeset_t *result = talloc (context, apply_changeset_t); result->callback = callback; result->thunk = thunk; result->escape_classes = escape_classes; result->report = talloc_zero (result, struct arch_apply_changeset_report); talloc_set_destructor (result->report, arch_free_apply_changeset_report_data); result->here_fd = safe_open (".", O_RDONLY, 0); result->mod_dir_id_of = NULL; result->new_dir_perms = NULL; result->removed_patch_conflict_files_path = NULL; result->deferred_conflicts = NULL; result->running.dir_loc_of = NULL; result->running.dir_id_of = NULL; result->running.file_loc_of = NULL; result->running.file_id_of = NULL; result->target = target; talloc_set_destructor (result, apply_changeset_destructor); return result; } /** * \brief free non talloc managed resources */ int apply_changeset_destructor (void * data) { apply_changeset_t * apply = talloc_get_type (data, apply_changeset_t); safe_close (apply->here_fd); arch_free_changeset_report_data (&apply->changeset); free_assoc_table (apply->mod_dir_id_of); free_assoc_table (apply->new_dir_perms); free_assoc_table (apply->running.dir_loc_of); free_assoc_table (apply->running.dir_id_of); free_assoc_table (apply->running.file_loc_of); free_assoc_table (apply->running.file_id_of); return 0; } struct arch_apply_changeset_report * arch_merge_from_changeset (t_uchar * changeset_spec, void * context, arch_project_tree_t * real_target_tree, enum arch_id_tagging_method method, enum arch_inventory_category untagged_source_category, assoc_table older_table, assoc_table yours_table, int escape_classes, int show_noops, arch_apply_changeset_report_callback callback, void * thunk) { apply_changeset_t * apply = arch_apply_changeset_new (talloc_context, callback, thunk, escape_classes, real_target_tree); struct arch_apply_changeset_report * result; int x; t_uchar * missing_patch_dir = 0; struct arch_changeset_inventory inventory; t_uchar * tmp_removed_files_root = 0; rel_table renamed_files_index = 0; rel_table present_renamed_files_index = 0; /* [0] tgtloc [1] origloc [2] modloc [3] id */ t_uchar * tmp_renamed_files_root = 0; rel_table renamed_dirs_index = 0; rel_table present_renamed_dirs_index = 0; /* [0] tgtloc [1] origloc [2] modloc [3] id [4] tmp_name (sort -r [0])*/ rel_table removed_dirs_index = 0; rel_table present_removed_dirs_index = 0; /* [0] tgtloc [1] id [2] tmp_name (sort -r [0]) */ t_uchar * tmp_shuffled_dirs_root = 0; rel_table dir_set_aside_with_dir_id = 0; /* [0] shuffled-dir-id [1] rel-loc-in-shuffled-dir [2] id */ rel_table file_set_aside_with_dir_id = 0; /* [0] shuffled-dir-id [1] rel-loc-in-shuffled-dir [2] id */ rel_table install_dirs_plan = 0; /* [0] modloc [1] path-or-empty-str [2] id [3] oldtgtloc */ rel_table added_files_and_symlinks = 0; rel_table patched_changelogs = 0; /* [0] final-loc [1] id [2] target_path */ read_changeset (apply, changeset_spec); missing_patch_dir = tmp_seq_file (apply->target->root, "++patches-missing-files"); /**************************************************************** * Study the changeset. */ apply->mod_dir_id_of = rel_to_assoc (apply->changeset.mod_dirs_index, 0, 1); apply->new_dir_perms = rel_to_assoc (apply->changeset.added_dirs, 0, 2); /**************************************************************** * Inventory the target tree. */ mem_set0 ((t_uchar *)&inventory, sizeof (inventory)); arch_changeset_inventory (&inventory, apply->target, method, untagged_source_category, escape_classes); setup_running (apply, &inventory); /**************************************************************** * Set aside and delete removed files. */ identify_removed_and_missing_removed_files (&apply->changeset, &inventory, apply->report); if (show_noops) for (x = 0; x < rel_n_records (apply->report->missing_removed_files); ++x) { one_path_callback (apply, "=D %s\n", apply->report->missing_removed_files[x][0]); } tmp_removed_files_root = rename_removed_files(talloc_context, apply->target, apply, &apply->running); /**************************************************************** * Set aside renamed files. */ renamed_files_index = rel_copy_table (apply->changeset.renamed_files); rel_sort_table_by_field (0, renamed_files_index, 2); present_renamed_files_index = rel_join (-1, rel_join_output (2,0, 1,0, 1,1, 2,1, -1), 2, 1, renamed_files_index, inventory.files); apply->report->missing_renamed_files = rel_join (1, rel_join_output (1,0, 1,1, 1,2, -1), 2, 1, renamed_files_index, inventory.files); for (x = 0; x < rel_n_records (apply->report->missing_renamed_files); ++x) two_path_callback (apply, "?r %s\n => %s\n", apply->report->missing_renamed_files[x][0], apply->report->missing_renamed_files[x][1]); rel_sort_table_by_field (0, apply->report->missing_removed_files, 0); tmp_renamed_files_root = talloc_tmp_file_name (tmp_removed_files_root, apply->target->root, ",,tmp-renamed-files"); rmrf_file (tmp_renamed_files_root); safe_mkdir (tmp_renamed_files_root, 0777); for (x = 0; x < rel_n_records (present_renamed_files_index); ++x) { t_uchar * target_loc = 0; t_uchar * target_id = 0; t_uchar * target_path = 0; t_uchar * dest_path = 0; t_uchar * dest_dir = 0; target_loc = present_renamed_files_index[x][0]; target_id = present_renamed_files_index[x][3]; target_path = file_name_in_vicinity (0, apply->target->root, target_loc); dest_path = file_name_in_vicinity (0, tmp_renamed_files_root, target_loc); dest_dir = file_name_directory_file (0, dest_path); ensure_directory_exists (dest_dir); safe_rename (target_path, dest_path); assoc_del (apply->running.file_id_of, target_loc); assoc_del (apply->running.file_loc_of, target_id); lim_free (0, target_path); lim_free (0, dest_path); lim_free (0, dest_dir); } /**************************************************************** * Set Aside Renamed and Removed Directories */ renamed_dirs_index = rel_copy_table (apply->changeset.renamed_dirs); rel_sort_table_by_field (0, renamed_dirs_index, 2); present_renamed_dirs_index = rel_join (-1, rel_join_output (2,0, 1,0, 1,1, 1,2, -1), 2, 1, renamed_dirs_index, inventory.dirs); rel_sort_table_by_field (1, present_renamed_dirs_index, 0); apply->report->missing_renamed_dirs = rel_join (1, rel_join_output (1,0, 1,1, 1,2, -1), 2, 1, renamed_dirs_index, inventory.dirs); rel_sort_table_by_field (0, apply->report->missing_renamed_dirs, 0); for (x = 0; x < rel_n_records (apply->report->missing_renamed_dirs); ++x) { two_path_callback (apply, "?r/ %s\n => %s\n", apply->report->missing_renamed_dirs[x][0], apply->report->missing_renamed_dirs[x][1]); } removed_dirs_index = rel_copy_table (apply->changeset.removed_dirs); rel_sort_table_by_field (0, removed_dirs_index, 1); present_removed_dirs_index = find_target_equivalent (removed_dirs_index, inventory.dirs); rel_sort_table_by_field (1, present_removed_dirs_index, 0); apply->report->missing_removed_dirs = find_target_missing(removed_dirs_index, inventory.dirs); tmp_shuffled_dirs_root = talloc_tmp_file_name (tmp_removed_files_root, apply->target->root, ",,tmp-shuffled-dirs"); rmrf_file (tmp_shuffled_dirs_root); safe_mkdir (tmp_shuffled_dirs_root, 0777); /* It's important to set aside shuffled dirs from deepest * to shallowest. */ { int seq; int ren_pos; int rem_pos; seq = 0; ren_pos = 0; rem_pos = 0; while (1) { int ren_done; int rem_done; int ren_first; t_uchar * target_loc; t_uchar * id; rel_record * dest_record; t_uchar * tmp_name; ren_done = (ren_pos >= rel_n_records (present_renamed_dirs_index)); rem_done = (rem_pos >= rel_n_records (present_removed_dirs_index)); if (ren_done && rem_done) break; ren_first = (rem_done || (!ren_done && (0 < str_cmp (present_renamed_dirs_index[ren_pos][0], present_removed_dirs_index[rem_pos][0])))); if (ren_first) { target_loc = present_renamed_dirs_index[ren_pos][0]; id = present_renamed_dirs_index[ren_pos][3]; dest_record = &present_renamed_dirs_index[ren_pos]; ++ren_pos; } else { target_loc = present_removed_dirs_index[rem_pos][0]; id = present_removed_dirs_index[rem_pos][1]; dest_record = &present_removed_dirs_index[rem_pos]; ++rem_pos; one_path_callback (apply, "D/ %s\n", target_loc); rel_add_records (&apply->report->removed_dirs, rel_make_record (target_loc, id, 0), 0); } tmp_name = set_aside_shuffled_dirs (&file_set_aside_with_dir_id, &dir_set_aside_with_dir_id, target_loc, id, seq, tmp_shuffled_dirs_root, &apply->running, apply->target->root, &inventory); ++seq; rel_add_field (dest_record, tmp_name); lim_free (0, tmp_name); } rel_sort_table_by_field (0, apply->report->removed_dirs, 0); rel_sort_table_by_field (0, dir_set_aside_with_dir_id, 0); rel_sort_table_by_field (0, file_set_aside_with_dir_id, 0); } if (show_noops) for (x = 0; x < rel_n_records (apply->report->missing_removed_dirs); ++x) { one_path_callback (apply, "=D/ %s\n", apply->report->missing_removed_dirs[x][0]); } /**************************************************************** * Make a name for a dir in which to stash removed .rej and .orig files */ apply->removed_patch_conflict_files_path = talloc_tmp_file_name (apply, apply->target->root, "+removed-conflict-files"); /**************************************************************** * What we have: * * We have the target tree, with all renamed/removed dirs and files * set aside. * * The removed files are all under $tmp_removed_files_root/$tgtloc. * * The renamed files are all under $tmp_renamed_files_root/$tgtloc. * * (in both of the above cases, $tgtloc is the original target loc) * * Both the renamed and removed directories are set aside * in $tmp_shuffled_dirs_root under integer filenames. * The tmp_name fields of present_renamed_dirs_index and * present_renamed_dirs_index are full paths to these temp * names. * * Todo: * * We have to install renamed and new directories, from shallowest to * deepest, then install renamed and new files (in any order). * * Each installed item has a destination path that has, in essense, * three parts: * * $tgthas / $newrelpath / $basename * * where $tgthas is the deepest part of the destination path that * the target tree already has when the item is installed, * $newrelpath are additional intermediate directories that need to * be created (possibly causing conflicts) and $basename is the final * name for the item (possibly causing a conflict). * * That three part path is derived from the $modloc of the item * by finding the deepest containing directory in $modloc which * is present (by id) in $target (that's $tgthas). * * In the code that follows: * * tgthas == $tgthas * install_dir == $tgthas / $newrelpath * install_name == $tgthas / $newrelpath / $basename * * Finally, then, we have to apply individual file and dir patches. */ /**************************************************************** * Compute an install plan for new and renamed directories. * * We have to add or rename containing dirs before contained. * * So, we need a plan for that. */ { rel_cut_spec spec = rel_cut_list (2, 4, 3, 0, -1); install_dirs_plan = rel_cut (spec, present_renamed_dirs_index); rel_cut_spec_finalise (&spec); } for (x = 0; x < rel_n_records (apply->changeset.added_dirs); ++x) { rel_add_records (&install_dirs_plan, rel_make_record (apply->changeset.added_dirs[x][0], "", apply->changeset.added_dirs[x][1], 0), 0); } rel_sort_table_by_field_fn (0, install_dirs_plan, 0, dir_depth_cmp); /**************************************************************** * Install dirs. */ for (x = 0; x < rel_n_records (install_dirs_plan); ++x) { t_uchar * mod_loc; t_uchar * take_from; t_uchar * id; t_uchar * target_has_dir = 0; t_uchar * target_has_path = 0; t_uchar * install_dir = 0; t_uchar * install_name = 0; t_uchar * install_loc = 0; mod_loc = install_dirs_plan[x][0]; take_from = install_dirs_plan[x][1]; id = install_dirs_plan[x][2]; if (!*take_from) take_from = 0; /* FIXME: trigger a conflict here on non-zero */ analyze_install (&apply->running, 1, 0, &target_has_dir, &install_dir, &install_name, &install_loc, apply->target->root, mod_loc, id, apply->mod_dir_id_of, file_set_aside_with_dir_id, dir_set_aside_with_dir_id); if (!str_cmp (target_has_dir, ".")) target_has_path = str_save (0, apply->target->root); else target_has_path = file_name_in_vicinity (0, apply->target->root, 2 + target_has_dir); /* over "./" */ ensure_directory_eliminating_conflicts (apply, target_has_path, install_dir); if (!take_from) { if (!safe_file_is_directory (install_name)) { int trust_umask = 0; int ign; t_uchar * perms_str; unsigned long perms; perms_str = assoc_ref (apply->new_dir_perms, mod_loc); if (cvt_octal_to_ulong (&ign, &perms, perms_str, str_length (perms_str))) { perms = 0777; trust_umask = 1; } if (!deferred_conflict (apply, install_loc, 0, &apply->running)) { one_path_callback (apply, "A/ %s\n", install_loc); rel_add_records (&apply->report->new_dirs, rel_make_record (install_loc, id, 0), 0); } else { one_path_callback (apply, "CA/ %s\n", install_loc); rel_add_records (&apply->report->conflict_dirs, rel_make_record (install_loc, id, 0), 0); } safe_mkdir (install_name, perms); if (!trust_umask) safe_chmod (install_name, perms); } } else { t_uchar * oldtgtloc; oldtgtloc = install_dirs_plan[x][3]; if (!deferred_conflict (apply, install_loc, 0, &apply->running)) { two_path_callback (apply, "/> %s\t%s\n", oldtgtloc, install_loc); rel_add_records (&apply->report->renamed_dirs, rel_make_record (oldtgtloc, install_loc, id, 0), 0); } else { two_path_callback (apply, "C/> %s\t%s\n", oldtgtloc, install_loc); rel_add_records (&apply->report->conflict_dirs, rel_make_record (install_loc, id, 0), 0); } safe_rename (take_from, install_name); } lim_free (0, target_has_dir); lim_free (0, target_has_path); lim_free (0, install_dir); lim_free (0, install_name); lim_free (0, install_loc); } rel_sort_table_by_field (0, apply->report->new_dirs, 0); rel_sort_table_by_field (0, apply->report->renamed_dirs, 0); /**************************************************************** * Install Renamed Files and Symlinks */ for (x = 0; x < rel_n_records (present_renamed_files_index); ++x) { t_uchar * old_target_loc; t_uchar * mod_loc; t_uchar * id; t_uchar * take_from = 0; t_uchar * target_has_dir = 0; t_uchar * install_dir = 0; t_uchar * install_name = 0; t_uchar * install_loc = 0; t_uchar * target_has_path = 0; old_target_loc = present_renamed_files_index[x][0]; mod_loc = present_renamed_files_index[x][2]; id = present_renamed_files_index[x][3]; take_from = file_name_in_vicinity (0, tmp_renamed_files_root, old_target_loc); /* renames cannot conflict on changed-paths */ invariant (!analyze_install (&apply->running, 0, 1, &target_has_dir, &install_dir, &install_name, &install_loc, apply->target->root, mod_loc, id, apply->mod_dir_id_of, 0, 0)); if (!str_cmp (target_has_dir, ".")) target_has_path = str_save (0, apply->target->root); else target_has_path = file_name_in_vicinity (0, apply->target->root, 2 + target_has_dir); /* over "./" */ ensure_directory_eliminating_conflicts (apply, target_has_path, install_dir); /* deferred conflicts are conflicts we need to do a rename on later */ if (!deferred_conflict (apply, install_loc, 0, &apply->running)) { two_path_callback (apply, "=> %s\t%s\n", old_target_loc, install_loc); rel_add_records (&apply->report->renamed_files, rel_make_record (old_target_loc, install_loc, id, 0), 0); } else { two_path_callback (apply, "C=> %s\t%s\n", old_target_loc, install_loc); rel_add_records (&apply->report->conflict_files, rel_make_record (install_loc, id, 0), 0); } safe_rename (take_from, install_name); lim_free (0, take_from); lim_free (0, target_has_dir); lim_free (0, install_dir); lim_free (0, install_name); lim_free (0, install_loc); lim_free (0, target_has_path); } added_files_and_symlinks = rel_copy_table (apply->changeset.added_files); rel_append_x (&added_files_and_symlinks, apply->changeset.added_symlinks); for (x = 0; x < rel_n_records (added_files_and_symlinks); ++x) { t_uchar * mod_loc; t_uchar * id; t_uchar * take_from; int path_conflict; t_uchar * target_has_dir = 0; t_uchar * install_dir = 0; t_uchar * install_name = 0; t_uchar * install_loc = 0; t_uchar * target_has_path = 0; mod_loc = added_files_and_symlinks[x][0]; id = added_files_and_symlinks[x][1]; take_from = added_files_and_symlinks[x][2]; path_conflict = analyze_install (&apply->running, 0, 0, &target_has_dir, &install_dir, &install_name, &install_loc, apply->target->root, mod_loc, id, apply->mod_dir_id_of, 0, 0); if (!str_cmp (target_has_dir, ".")) target_has_path = str_save (0, apply->target->root); else target_has_path = file_name_in_vicinity (0, apply->target->root, 2 + target_has_dir); /* over "./" */ ensure_directory_eliminating_conflicts (apply, target_has_path, install_dir); if (path_conflict) { one_path_callback (apply, "CA %s (id present already)\n", install_loc); rel_add_records (&apply->report->conflict_files, rel_make_record (install_loc, id, 0), 0); rel_add_records (&apply->report->conflict_files, rel_make_record (assoc_ref (apply->running.file_loc_of, id), id, 0), 0); } else if (!deferred_conflict (apply, install_loc, take_from, &apply->running)) { one_path_callback (apply, "A %s\n", install_loc); rel_add_records (&apply->report->new_files, rel_make_record (install_loc, id, 0), 0); } else { one_path_callback (apply, "CA %s\n", install_loc); rel_add_records (&apply->report->conflict_files, rel_make_record (install_loc, id, 0), 0); } copy_file_or_symlink (take_from, install_name); copy_permissions (take_from, install_name); lim_free (0, target_has_dir); lim_free (0, install_dir); lim_free (0, install_name); lim_free (0, install_loc); lim_free (0, target_has_path); } rel_sort_table_by_field (0, apply->report->renamed_files, 0); /**************************************************************** * Patch Regular Files */ for (x = 0; x < rel_n_records (apply->changeset.patched_regular_files); ++x) { t_uchar * mod_loc; t_uchar * id; t_uchar * target_loc; t_uchar * patch_path = 0; t_uchar * target_path = 0; struct stat target_stat; patch_path = str_alloc_cat (0, apply->changeset.patched_regular_files[x][2], ".patch"); mod_loc = apply->changeset.patched_regular_files[x][0]; id = apply->changeset.patched_regular_files[x][1]; target_loc = assoc_ref (apply->running.file_loc_of, id); if (!target_loc) { one_path_callback (apply, "?M %s\n", mod_loc); rel_add_records (&apply->report->missing_file_for_patch, rel_make_record (mod_loc, id, 0), 0); save_patch_for_missing_file (missing_patch_dir, patch_path, mod_loc); continue; } target_path = file_name_in_vicinity (0, apply->target->root, target_loc); safe_lstat (target_path, &target_stat); preserve_old_patch_spew (apply->removed_patch_conflict_files_path, apply->target, target_loc, &apply->running); if (S_ISLNK (target_stat.st_mode)) { t_uchar * orig_name; t_uchar * rej_name; conflict_filenames (apply->target->root, target_path, &orig_name, &rej_name, &apply->running); safe_rename (target_path, orig_name); copy_file (patch_path, rej_name); copy_permissions (patch_path, rej_name); one_path_callback (apply, "C-> %s\n", target_loc); rel_add_records (&apply->report->conflict_files, rel_make_record (target_loc, id, 0), 0); } else if (arch_id_indicates_changelog (id)) { rel_add_records (&patched_changelogs, rel_make_record (target_loc, id, target_path, 0), 0); } else { struct stat patch_file_stat; safe_stat (patch_path, &patch_file_stat); if (patch_file_stat.st_size) { int errn; t_uchar * target_dir = 0; t_uchar * basename = 0; t_uchar * original_inode_basename = 0; t_uchar * original_inode_tmpname = 0; int patch_stat = -1; target_dir = file_name_directory_file (0, target_path); basename = file_name_tail (0, target_path); original_inode_basename = str_alloc_cat_many (0, ",,dopatch.", basename, ".", str_end); safe_chdir (target_dir); original_inode_tmpname = talloc_tmp_file_name (talloc_context, ".", original_inode_basename); safe_rename (basename, original_inode_tmpname); if (vu_unlink (&errn, ",,patch-output") && (errn != ENOENT)) { safe_printfmt (2, "arch_apply_changeset: unable to unlink file\n"); safe_printfmt (2, " file: %s/,,patch-output\n", target_dir); safe_printfmt (2, " error: %s\n", errno_to_string (errn)); exit (2); } { t_uchar * older_path = assoc_ref (older_table, id); t_uchar * yours_path = assoc_ref (yours_table, id); patch_stat = run_diff3 (basename, original_inode_tmpname, older_path, yours_path); } if (patch_stat == 0) { int was_noop = !arch_binary_files_differ (original_inode_tmpname, basename, 0, 0); copy_permissions (original_inode_tmpname, basename); safe_unlink (original_inode_tmpname); if (was_noop) { if (show_noops) one_path_callback (apply, "=M %s\n", target_loc); } else one_path_callback (apply, "M %s\n", target_loc); rel_add_records (&apply->report->modified_files, rel_make_record (target_loc, id, 0), 0); } else if (patch_stat == 1) { t_uchar * orig_name; copy_permissions (original_inode_tmpname, basename); conflict_filenames (apply->target->root, target_path, &orig_name, NULL, &apply->running); safe_rename (original_inode_tmpname, orig_name); one_path_callback (apply, "C %s\n", target_loc); rel_add_records (&apply->report->conflict_files, rel_make_record (target_loc, id, 0), 0); lim_free (0, orig_name); } else { int in_fd; safe_printfmt (2, "arch_apply_changeset: internal error (patch returned odd status)\n"); safe_printfmt (2, " patch exit status: %d\n", patch_stat); safe_printfmt (2, " target file: %s\n", target_path); safe_printfmt (2, "\n"); in_fd = safe_open (",,patch-output", O_RDONLY, 0); copy_fd (in_fd, 2); safe_printfmt (2, "\n"); exit (2); } if (vu_unlink (&errn, ",,patch-output") && (errn != ENOENT)) { safe_printfmt (2, "arch_apply_changeset: unable to unlink file\n"); safe_printfmt (2, " file: %s/,,patch-output\n", target_dir); safe_printfmt (2, " error: %s\n", errno_to_string (errn)); exit (2); } safe_fchdir (apply->here_fd); lim_free (0, target_dir); lim_free (0, basename); lim_free (0, original_inode_basename); talloc_free (original_inode_tmpname); } } lim_free (0, patch_path); lim_free (0, target_path); } /**************************************************************** * Patch Symlinks */ for (x = 0; x < rel_n_records (apply->changeset.patched_symlinks); ++x) { t_uchar * target_loc; t_uchar * target_path = 0; struct stat target_stat; t_uchar * orig_patch_path = str_alloc_cat (0, apply->changeset.patched_symlinks[x][2], ".link-orig"); t_uchar * mod_patch_path = str_alloc_cat (0, apply->changeset.patched_symlinks[x][2], ".link-mod"); t_uchar * mod_loc = apply->changeset.patched_symlinks[x][0]; t_uchar * id = apply->changeset.patched_symlinks[x][1]; target_loc = assoc_ref (apply->running.file_loc_of, id); if (!target_loc) { one_path_callback (apply, "?M %s\n", mod_loc); rel_add_records (&apply->report->missing_file_for_patch, rel_make_record (mod_loc, id, 0), 0); save_patch_for_missing_file (missing_patch_dir, orig_patch_path, mod_loc); save_patch_for_missing_file (missing_patch_dir, mod_patch_path, mod_loc); continue; } target_path = file_name_in_vicinity (0, apply->target->root, target_loc); safe_lstat (target_path, &target_stat); preserve_old_patch_spew (apply->removed_patch_conflict_files_path, apply->target, target_loc, &apply->running); if (!S_ISLNK (target_stat.st_mode)) { t_uchar * orig_name = 0; t_uchar * rej_name = 0; int out_fd; int in_fd; symlink_conflict: conflict_filenames (apply->target->root, target_path, &orig_name, &rej_name, &apply->running); safe_rename (target_path, orig_name); out_fd = safe_open (rej_name, O_WRONLY | O_CREAT | O_EXCL, 0666); safe_printfmt (out_fd, "Patched wanted to retarget a symbolic link:\n\n"); safe_printfmt (out_fd, " from: "); in_fd = safe_open (orig_patch_path, O_RDONLY, 0); copy_fd (in_fd, out_fd); safe_close (in_fd); safe_printfmt (out_fd, "\n to: "); in_fd = safe_open (mod_patch_path, O_RDONLY, 0); copy_fd (in_fd, out_fd); safe_close (in_fd); safe_printfmt (out_fd, "\n"); one_path_callback (apply, "C-> %s\n", target_loc); rel_add_records (&apply->report->conflict_files, rel_make_record (target_loc, id, 0), 0); lim_free (0, orig_name); lim_free (0, rej_name); } else { /**************************************************************** * FIX THIS * * Strictly speaking: * * We should really try to "remap" the orig and mod link targets for * the current tree layout. */ t_uchar * orig_link_target = 0; t_uchar * mod_link_target = 0; t_uchar * tree_link_target = 0; int patch_stat; orig_link_target = read_line_from_file (orig_patch_path); mod_link_target = read_line_from_file (mod_patch_path); tree_link_target = link_target (target_path); if (!str_cmp (mod_link_target, tree_link_target)) { patch_stat = 0; } else if (!str_cmp (orig_link_target, tree_link_target)) { t_uchar * target_path_dir = 0; t_uchar * target_path_tail = 0; t_uchar * tmp_file_basename = 0; t_uchar * tmp_file_path = 0; target_path_dir = file_name_directory_file (0, target_path); target_path_tail = file_name_tail (0, target_path); tmp_file_basename = str_alloc_cat_many (0, ",,dopatch.", target_path_tail, ".", str_end); tmp_file_path = talloc_tmp_file_name (talloc_context, target_path_dir, tmp_file_basename); rmrf_file (tmp_file_path); safe_rename (target_path, tmp_file_path); safe_symlink (mod_link_target, target_path); safe_unlink (tmp_file_path); patch_stat = 0; lim_free (0, target_path_dir); lim_free (0, target_path_tail); lim_free (0, tmp_file_basename); talloc_free (tmp_file_path); } else { patch_stat = 1; } if (patch_stat == 0) { one_path_callback (apply, "M-> %s\n", target_loc); rel_add_records (&apply->report->modified_files, rel_make_record (target_loc, id, 0), 0); } else goto symlink_conflict; lim_free (0, orig_link_target); lim_free (0, mod_link_target); lim_free (0, tree_link_target); } lim_free (0, orig_patch_path); lim_free (0, mod_patch_path); lim_free (0, target_path); } /**************************************************************** * Patch Binaries */ for (x = 0; x < rel_n_records (apply->changeset.patched_binaries); ++x) { t_uchar * target_path = 0; struct stat target_stat; t_uchar * orig_patch_path = str_alloc_cat (0, apply->changeset.patched_binaries[x][2], ".original"); t_uchar * mod_patch_path = str_alloc_cat (0, apply->changeset.patched_binaries[x][2], ".modified"); t_uchar * mod_loc = apply->changeset.patched_binaries[x][0]; t_uchar * id = apply->changeset.patched_binaries[x][1]; t_uchar * target_loc = assoc_ref (apply->running.file_loc_of, id); if (!target_loc) { one_path_callback (apply, "?M %s\n", mod_loc); rel_add_records (&apply->report->missing_file_for_patch, rel_make_record (mod_loc, id, 0), 0); save_patch_for_missing_file (missing_patch_dir, orig_patch_path, mod_loc); save_patch_for_missing_file (missing_patch_dir, mod_patch_path, mod_loc); continue; } target_path = file_name_in_vicinity (0, apply->target->root, target_loc); safe_lstat (target_path, &target_stat); preserve_old_patch_spew (apply->removed_patch_conflict_files_path, apply->target, target_loc, &apply->running); if (S_ISLNK (target_stat.st_mode)) { t_uchar * orig_name; t_uchar * rej_name; binary_conflict: conflict_filenames (apply->target->root, target_path, &orig_name, &rej_name, &apply->running); safe_rename (target_path, orig_name); copy_file (mod_patch_path, rej_name); copy_permissions (mod_patch_path, rej_name); one_path_callback (apply, "Cb %s\n", target_loc); rel_add_records (&apply->report->conflict_files, rel_make_record (target_loc, id, 0), 0); lim_free (0, orig_name); lim_free (0, rej_name); } else { int patch_stat; if (arch_binary_files_differ (orig_patch_path, target_path, 0, 0)) { patch_stat = 1; } else { t_uchar * target_path_dir = 0; t_uchar * target_path_tail = 0; t_uchar * tmp_file_basename = 0; t_uchar * tmp_file_path = 0; target_path_dir = file_name_directory_file (0, target_path); target_path_tail = file_name_tail (0, target_path); tmp_file_basename = str_alloc_cat_many (0, ",,dopatch.", target_path_tail, ".", str_end); tmp_file_path = talloc_tmp_file_name (talloc_context, target_path_dir, tmp_file_basename); rmrf_file (tmp_file_path); safe_rename (target_path, tmp_file_path); copy_file (mod_patch_path, target_path); copy_permissions (mod_patch_path, target_path); safe_unlink (tmp_file_path); patch_stat = 0; lim_free (0, target_path_dir); lim_free (0, target_path_tail); lim_free (0, tmp_file_basename); talloc_free (tmp_file_path); } if (patch_stat == 0) { one_path_callback (apply, "Mb %s\n", target_loc); rel_add_records (&apply->report->modified_files, rel_make_record (target_loc, id, 0), 0); } else goto binary_conflict; } lim_free (0, orig_patch_path); lim_free (0, mod_patch_path); lim_free (0, target_path); } /**************************************************************** * Symlinks->File Patches */ for (x = 0; x < rel_n_records (apply->changeset.symlink_to_file); ++x) { t_uchar * orig_patch_path = 0; t_uchar * mod_patch_path = 0; t_uchar * target_path = 0; t_uchar * mod_loc; t_uchar * id; t_uchar * target_loc; struct stat target_stat; orig_patch_path = str_alloc_cat (0, apply->changeset.symlink_to_file[x][2], ".link-orig"); mod_patch_path = str_alloc_cat (0, apply->changeset.symlink_to_file[x][2], ".modified"); mod_loc = apply->changeset.symlink_to_file[x][0]; id = apply->changeset.symlink_to_file[x][1]; target_loc = assoc_ref (apply->running.file_loc_of, id); if (!target_loc) { one_path_callback (apply, "?M %s\n", mod_loc); rel_add_records (&apply->report->missing_file_for_patch, rel_make_record (mod_loc, id, 0), 0); save_patch_for_missing_file (missing_patch_dir, orig_patch_path, mod_loc); save_patch_for_missing_file (missing_patch_dir, mod_patch_path, mod_loc); continue; } target_path = file_name_in_vicinity (0, apply->target->root, target_loc); safe_lstat (target_path, &target_stat); preserve_old_patch_spew (apply->removed_patch_conflict_files_path, apply->target, target_loc, &apply->running); if (!S_ISLNK (target_stat.st_mode)) { t_uchar * orig_name; t_uchar * rej_name; symlink_to_file_conflict: conflict_filenames (apply->target->root, target_path, &orig_name, &rej_name, &apply->running); safe_rename (target_path, orig_name); copy_file (mod_patch_path, rej_name); copy_permissions (mod_patch_path, rej_name); one_path_callback (apply, "Cch %s\n", target_loc); rel_add_records (&apply->report->conflict_files, rel_make_record (target_loc, id, 0), 0); lim_free (0, orig_name); lim_free (0, rej_name); } else { t_uchar * orig_link_target = 0; t_uchar * tree_link_target = 0; int patch_stat; orig_link_target = read_line_from_file (orig_patch_path); tree_link_target = link_target (target_path); if (str_cmp (orig_link_target, tree_link_target)) { patch_stat = 1; } else { t_uchar * target_path_dir = 0; t_uchar * target_path_tail = 0; t_uchar * tmp_file_basename = 0; t_uchar * tmp_file_path = 0; target_path_dir = file_name_directory_file (0, target_path); target_path_tail = file_name_tail (0, target_path); tmp_file_basename = str_alloc_cat_many (0, ",,dopatch.", target_path_tail, ".", str_end); tmp_file_path = talloc_tmp_file_name (talloc_context, target_path_dir, tmp_file_basename); rmrf_file (tmp_file_path); safe_rename (target_path, tmp_file_path); copy_file (mod_patch_path, target_path); copy_permissions (mod_patch_path, target_path); safe_unlink (tmp_file_path); patch_stat = 0; lim_free (0, target_path_dir); lim_free (0, target_path_tail); lim_free (0, tmp_file_basename); talloc_free (tmp_file_path); } if (patch_stat == 0) { one_path_callback (apply, "ch %s\n", target_loc); rel_add_records (&apply->report->modified_files, rel_make_record (target_loc, id, 0), 0); } else goto symlink_to_file_conflict; lim_free (0, orig_link_target); lim_free (0, tree_link_target); } lim_free (0, orig_patch_path); lim_free (0, mod_patch_path); lim_free (0, target_path); } /**************************************************************** * File->Symlink Patches */ for (x = 0; x < rel_n_records (apply->changeset.file_to_symlink); ++x) { t_uchar * target_path = 0; struct stat target_stat; t_uchar * orig_patch_path = str_alloc_cat (0, apply->changeset.file_to_symlink[x][2], ".original"); t_uchar * mod_patch_path = str_alloc_cat (0, apply->changeset.file_to_symlink[x][2], ".link-mod"); t_uchar * mod_loc = apply->changeset.file_to_symlink[x][0]; t_uchar * id = apply->changeset.file_to_symlink[x][1]; t_uchar * target_loc = assoc_ref (apply->running.file_loc_of, id); if (!target_loc) { one_path_callback (apply, "?M %s\n",mod_loc); rel_add_records (&apply->report->missing_file_for_patch, rel_make_record (mod_loc, id, 0), 0); save_patch_for_missing_file (missing_patch_dir, orig_patch_path, mod_loc); save_patch_for_missing_file (missing_patch_dir, mod_patch_path, mod_loc); continue; } target_path = file_name_in_vicinity (0, apply->target->root, target_loc); safe_lstat (target_path, &target_stat); preserve_old_patch_spew (apply->removed_patch_conflict_files_path, apply->target, target_loc, &apply->running); if (S_ISLNK (target_stat.st_mode)) { t_uchar * orig_name; t_uchar * rej_name; int out_fd; int in_fd; file_to_symlink_conflict: conflict_filenames (apply->target->root, target_path, &orig_name, &rej_name, &apply->running); safe_rename (target_path, orig_name); out_fd = safe_open (rej_name, O_WRONLY | O_CREAT | O_EXCL, 0666); safe_printfmt (out_fd, "MOD had a symlink to: "); in_fd = safe_open (mod_patch_path, O_RDONLY, 0); copy_fd (in_fd, out_fd); safe_close (in_fd); safe_close (out_fd); one_path_callback (apply, "Cch %s\n", target_loc); rel_add_records (&apply->report->conflict_files, rel_make_record (target_loc, id, 0), 0); lim_free (0, orig_name); lim_free (0, rej_name); } else { t_uchar * mod_link_target = 0; t_uchar * tree_link_target = 0; int patch_stat; mod_link_target = read_line_from_file (mod_patch_path); if (arch_binary_files_differ (orig_patch_path, target_path, 0, 0)) { patch_stat = 1; } else { t_uchar * target_path_dir = 0; t_uchar * target_path_tail = 0; t_uchar * tmp_file_basename = 0; t_uchar * tmp_file_path = 0; target_path_dir = file_name_directory_file (0, target_path); target_path_tail = file_name_tail (0, target_path); tmp_file_basename = str_alloc_cat_many (0, ",,dopatch.", target_path_tail, ".", str_end); tmp_file_path = talloc_tmp_file_name (talloc_context, target_path_dir, tmp_file_basename); rmrf_file (tmp_file_path); safe_rename (target_path, tmp_file_path); safe_symlink (mod_link_target, target_path); safe_unlink (tmp_file_path); patch_stat = 0; lim_free (0, target_path_dir); lim_free (0, target_path_tail); lim_free (0, tmp_file_basename); talloc_free (tmp_file_path); } if (patch_stat == 0) { one_path_callback (apply, "ch %s\n", target_loc); rel_add_records (&apply->report->modified_files, rel_make_record (target_loc, id, 0), 0); } else goto file_to_symlink_conflict; lim_free (0, mod_link_target); lim_free (0, tree_link_target); } lim_free (0, orig_patch_path); lim_free (0, mod_patch_path); lim_free (0, target_path); } /**************************************************************** * Patch File Metadata */ for (x = 0; x < rel_n_records (apply->changeset.file_metadata_changed); ++x) { t_uchar * target_path = 0; t_uchar * target_spew_loc = 0; t_uchar * target_spew_path = 0; struct stat target_stat; t_uchar * orig_patch_path = str_alloc_cat (0, apply->changeset.file_metadata_changed[x][2], ".meta-orig"); t_uchar * mod_patch_path = str_alloc_cat (0, apply->changeset.file_metadata_changed[x][2], ".meta-mod"); t_uchar * mod_loc = apply->changeset.file_metadata_changed[x][0]; t_uchar * id = apply->changeset.file_metadata_changed[x][1]; t_uchar * target_loc = assoc_ref (apply->running.file_loc_of, id); if (!target_loc) { one_path_callback (apply, "?-- %s\n", mod_loc); rel_add_records (&apply->report->missing_file_for_meta_patch, rel_make_record (mod_loc, id, 0), 0); save_patch_for_missing_file (missing_patch_dir, orig_patch_path, mod_loc); save_patch_for_missing_file (missing_patch_dir, mod_patch_path, mod_loc); continue; } target_path = file_name_in_vicinity (0, apply->target->root, target_loc); target_spew_loc = str_alloc_cat (0, target_loc, ".meta"); target_spew_path = file_name_in_vicinity (0, apply->target->root, target_spew_loc); preserve_old_patch_spew (apply->removed_patch_conflict_files_path, apply->target, target_spew_loc, &apply->running); safe_lstat (target_path, &target_stat); if (S_ISLNK (target_stat.st_mode)) { t_uchar * rej_name; int out_fd; int in_fd; rej_name = str_alloc_cat (0, target_spew_path, ".rej"); out_fd = safe_open (rej_name, O_WRONLY | O_CREAT | O_EXCL, 0666); safe_printfmt (out_fd, "MOD had a metadata change: "); in_fd = safe_open (mod_patch_path, O_RDONLY, 0); copy_fd (in_fd, out_fd); safe_close (in_fd); safe_close (out_fd); one_path_callback (apply, "C-- %s\n", target_loc); rel_add_records (&apply->report->metadata_conflict_files, rel_make_record (target_loc, id, 0), 0); lim_free (0, rej_name); } else { mode_t orig_perms; mode_t mod_perms; mod_perms = arch_read_permissions_patch (mod_patch_path); orig_perms = arch_read_permissions_patch (orig_patch_path); if (mod_perms != (target_stat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO))) { t_uchar * target_path_dir = 0; t_uchar * target_path_tmp = 0; target_path_dir = file_name_directory_file (0, target_path); target_path_tmp = talloc_tmp_file_name (talloc_context, target_path_dir, ",,meta-tmp"); rmrf_file (target_path_tmp); copy_file (target_path, target_path_tmp); safe_chmod (target_path_tmp, mod_perms); safe_rename (target_path_tmp, target_path); lim_free (0, target_path_dir); talloc_free (target_path_tmp); } one_path_callback (apply, "-- %s\n", target_loc); rel_add_records (&apply->report->meta_modified_files, rel_make_record (target_loc, id, 0), 0); } lim_free (0, orig_patch_path); lim_free (0, mod_patch_path); lim_free (0, target_path); lim_free (0, target_spew_loc); lim_free (0, target_spew_path); } /**************************************************************** * Patch Dir Metadata */ for (x = 0; x < rel_n_records (apply->changeset.dir_metadata_changed); ++x) { t_uchar * target_loc; t_uchar * target_path = 0; t_uchar * target_spew_loc = 0; t_uchar * target_spew_path = 0; struct stat target_stat; t_uchar * orig_patch_path = file_name_in_vicinity (0, apply->changeset.dir_metadata_changed[x][2], "=dir-meta-orig"); t_uchar * mod_patch_path = file_name_in_vicinity (0, apply->changeset.dir_metadata_changed[x][2], "=dir-meta-mod"); t_uchar * mod_loc = apply->changeset.dir_metadata_changed[x][0]; t_uchar * id = apply->changeset.dir_metadata_changed[x][1]; target_loc = assoc_ref (apply->running.dir_loc_of, id); if (!target_loc) { one_path_callback (apply, "?-/ %s\n", mod_loc); rel_add_records (&apply->report->missing_dir_for_meta_patch, rel_make_record (mod_loc, id, 0), 0); save_patch_for_missing_file (missing_patch_dir, orig_patch_path, mod_loc); save_patch_for_missing_file (missing_patch_dir, mod_patch_path, mod_loc); continue; } target_path = file_name_in_vicinity (0, apply->target->root, target_loc); target_spew_loc = str_alloc_cat (0, target_loc, ".meta"); target_spew_path = file_name_in_vicinity (0, apply->target->root, target_spew_loc); preserve_old_patch_spew (apply->removed_patch_conflict_files_path, apply->target, target_spew_loc, &apply->running); safe_stat (target_path, &target_stat); { mode_t orig_perms; mode_t mod_perms; mod_perms = arch_read_permissions_patch (mod_patch_path); orig_perms = arch_read_permissions_patch (orig_patch_path); if (mod_perms != (target_stat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO))) { safe_chmod (target_path, mod_perms); } one_path_callback (apply, "--/ %s\n", target_loc); rel_add_records (&apply->report->meta_modified_dirs, rel_make_record (target_loc, id, 0), 0); } lim_free (0, orig_patch_path); lim_free (0, mod_patch_path); lim_free (0, target_path); lim_free (0, target_spew_loc); lim_free (0, target_spew_path); } /**************************************************************** * Update Changelogs */ for (x = 0; x < rel_n_records (patched_changelogs); ++x) { t_uchar * target_loc; t_uchar * id; t_uchar * target_path; t_uchar * target_dir = 0; t_uchar * target_tmp = 0; t_uchar * archive = 0; t_uchar * version = 0; int out_fd; struct stat stat_was; mode_t mode; target_loc = patched_changelogs[x][0]; id = patched_changelogs[x][1]; target_path = patched_changelogs[x][2]; target_dir = file_name_directory_file (0, target_path); target_tmp = talloc_tmp_file_name (talloc_context, target_dir, ",,new-changeset"); safe_stat (target_path, &stat_was); mode = (stat_was.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)); arch_parse_changelog_id (&archive, &version, id); rmrf_file (target_tmp); out_fd = safe_open (target_tmp, O_WRONLY | O_EXCL | O_CREAT, mode); safe_fchmod (out_fd, mode); safe_buffer_fd (out_fd, 0, O_WRONLY, 0); arch_generate_changelog (out_fd, apply->target, 0, 0, 0, 0, archive, version); safe_close (out_fd); safe_rename (target_tmp, target_path); one_path_callback (apply, "cl %s\n", target_loc); rel_add_records (&apply->report->modified_files, rel_make_record (target_loc, id, 0), 0); lim_free (0, target_dir); talloc_free (target_tmp); lim_free (0, archive); lim_free (0, version); } /**************************************************************** * Finish Up Deferred Conflicts */ rel_sort_table_by_field (1, apply->deferred_conflicts, 0); for (x = 0; x < rel_n_records (apply->deferred_conflicts); ++x) { t_uchar * target_path = 0; t_uchar * rej_path = 0; target_path = file_name_in_vicinity (0, apply->target->root, apply->deferred_conflicts[x][0]); rej_path = str_alloc_cat (0, target_path, ".rej"); safe_rename (target_path, rej_path); lim_free (0, target_path); lim_free (0, rej_path); } report_sort_and_unique (apply->report); arch_tree_note_conflicts (apply->target, apply->report); arch_project_tree_mutated (apply->target); /**************************************************************** * cleanup */ lim_free (0, missing_patch_dir); arch_free_changeset_inventory_data (&inventory); rmrf_file (tmp_removed_files_root); rmrf_file (tmp_renamed_files_root); rmrf_file (tmp_shuffled_dirs_root); talloc_free (tmp_removed_files_root); rel_free_table (renamed_files_index); rel_free_table (present_renamed_files_index); rel_free_table (renamed_dirs_index); rel_free_table (present_renamed_dirs_index); rel_free_table (removed_dirs_index); rel_free_table (present_removed_dirs_index); rel_free_table (file_set_aside_with_dir_id); rel_free_table (dir_set_aside_with_dir_id); rel_free_table (install_dirs_plan); rel_free_table (added_files_and_symlinks); rel_free_table (patched_changelogs); result = talloc_steal (context, apply->report); talloc_free (apply); return result; } static void invoke_apply_changeset_callback (apply_changeset_t * apply, t_uchar * fmt, ...) { va_list ap; if (apply->callback) { va_start (ap, fmt); apply->callback (apply->thunk, fmt, ap); va_end (ap); } } void one_path_callback (apply_changeset_t * apply, t_uchar * fmt, t_uchar *path) { if (apply->callback) { t_uchar * escaped_path = pika_save_escape_iso8859_1 (0, 0, apply->escape_classes, path); invoke_apply_changeset_callback (apply, fmt, no_dot (escaped_path)); lim_free (0, escaped_path); } } void two_path_callback (apply_changeset_t * apply, t_uchar * fmt, t_uchar *path1, t_uchar *path2) { if (apply->callback) { t_uchar * escaped_path1 = pika_save_escape_iso8859_1 (0, 0, apply->escape_classes, path1); t_uchar * escaped_path2 = pika_save_escape_iso8859_1 (0, 0, apply->escape_classes, path2); invoke_apply_changeset_callback (apply, fmt, no_dot (escaped_path1), no_dot (escaped_path2)); lim_free (0, escaped_path1); lim_free (0, escaped_path2); } } static t_uchar * set_aside_shuffled_dirs (rel_table * file_set_aside_with, rel_table * dir_set_aside_with, t_uchar * target_loc, t_uchar * id, int seq_n, t_uchar * dest_root, struct running_inventory_assocs * running, t_uchar * target, struct arch_changeset_inventory * inv) { t_uchar * target_path = 0; t_uchar * seq = 0; t_uchar * dest_path = 0; t_uchar * target_loc_as_dir = 0; size_t target_loc_as_dir_len; int y; target_path = file_name_in_vicinity (0, target, target_loc); seq = int_to_string (seq_n); dest_path = file_name_in_vicinity (0, dest_root, seq); ensure_directory_exists (dest_root); safe_rename (target_path, dest_path); target_loc_as_dir = file_name_as_directory (0, target_loc); target_loc_as_dir_len = str_length (target_loc_as_dir); for (y = 0; y < rel_n_records (inv->files); ++y) { if (!str_cmp_prefix (target_loc_as_dir, inv->files[y][0])) { if (assoc_ref (running->file_id_of, inv->files[y][0])) { assoc_del (running->file_id_of, inv->files[y][0]); assoc_del (running->file_loc_of, inv->files[y][1]); rel_add_records (file_set_aside_with, rel_make_record (id, inv->files[y][0] + target_loc_as_dir_len, inv->files[y][1], 0), 0); } } } for (y = 0; y < rel_n_records (inv->dirs); ++y) { if (!str_cmp_prefix (target_loc_as_dir, inv->dirs[y][0])) { if (assoc_ref (running->dir_id_of, inv->dirs[y][0])) { assoc_del (running->dir_id_of, inv->dirs[y][0]); assoc_del (running->dir_loc_of, inv->dirs[y][1]); rel_add_records (dir_set_aside_with, rel_make_record (id, inv->dirs[y][0] + target_loc_as_dir_len, inv->dirs[y][1], 0), 0); } } } assoc_del (running->dir_id_of, target_loc); assoc_del (running->dir_loc_of, id); lim_free (0, target_path); lim_free (0, seq); lim_free (0, target_loc_as_dir); return dest_path; } void preserve_old_patch_spew (t_uchar * dest_root, arch_project_tree_t * target, t_uchar * loc, struct running_inventory_assocs *inventory) { t_uchar * target_path = 0; t_uchar * orig_path = 0; t_uchar * rej_path = 0; t_uchar * dest_path = 0; t_uchar * dest_dir; t_uchar * orig_dest = 0; t_uchar * rej_dest = 0; target_path = file_name_in_vicinity (0, target->root, loc); conflict_filenames (target->root, target_path, &orig_path, &rej_path, inventory); dest_path = file_name_in_vicinity (0, dest_root, loc); dest_dir = file_name_directory_file (0, dest_path); conflict_filenames (dest_root, dest_path, &orig_dest, &rej_dest, inventory); if (!safe_access (orig_path, F_OK)) { ensure_directory_exists (dest_dir); safe_rename (orig_path, orig_dest); } if (!safe_access (rej_path, F_OK)) { ensure_directory_exists (dest_dir); safe_rename (rej_path, rej_dest); } lim_free (0, target_path); lim_free (0, orig_path); lim_free (0, rej_path); lim_free (0, dest_path); lim_free (0, dest_dir); lim_free (0, orig_dest); lim_free (0, rej_dest); } /** * \brief detect whether a new file is going to overwrite an old one, and if so is the content different * */ static int deferred_conflict (apply_changeset_t * apply, t_uchar * loc, t_uchar * orig_copy, struct running_inventory_assocs *inventory) { t_uchar * path = 0; t_uchar * orig_path = 0; int conflict_detected; struct stat target_stat; int ign; path = file_name_in_vicinity (0, apply->target->root, loc); conflict_filenames (apply->target->root, path, &orig_path, NULL, inventory); conflict_detected = 0; if (!vu_lstat(&ign, path, &target_stat)) { struct stat orig_stat; if (orig_copy) { safe_lstat (orig_copy, &orig_stat); if (S_ISREG (orig_stat.st_mode) && S_ISREG (target_stat.st_mode) && !arch_binary_files_differ (orig_copy, path, 0, 0)) { safe_unlink (path); } else if (S_ISLNK (orig_stat.st_mode) && S_ISLNK (target_stat.st_mode) && !arch_symlinks_differ (orig_copy, path)) { safe_unlink (path); } else goto conflict; } else { conflict: preserve_old_patch_spew (apply->removed_patch_conflict_files_path, apply->target, loc, inventory); rel_add_records (&apply->deferred_conflicts, rel_make_record (loc, 0), 0); talloc_steal (apply, ar_base (apply->deferred_conflicts)); safe_rename (path, orig_path); conflict_detected = 1; } } lim_free (0, path); lim_free (0, orig_path); return conflict_detected; } static int dir_depth_cmp (t_uchar * a, t_uchar * b) { return str_cmp (a, b); } /** * \brief figure out whats involved in installing a new file, and update the running inventory * FIXME (Hard): we need to figure out what to do with conflict adds to different paths * i.e. add BAR, when FOO exists, with the same id - the running inventory isn't sufficient * \return non-zero if there is a path-id conflict with the install */ static int analyze_install (struct running_inventory_assocs * running, int is_dir, int is_rename, t_uchar ** target_has_dir, t_uchar ** install_dir, t_uchar ** install_name, t_uchar ** install_loc, t_uchar * target, t_uchar * mod_loc, t_uchar * id, assoc_table mod_dir_id_of, rel_table file_set_aside_with, rel_table dir_set_aside_with) { t_uchar * basename = 0; t_uchar * loc_dir = 0; int result = 0; basename = file_name_tail (0, mod_loc); loc_dir = file_name_directory_file (0, mod_loc); if (!str_cmp (loc_dir, ".")) { *target_has_dir = str_save (0, "."); *install_dir = str_save (0, target); *install_name = file_name_in_vicinity (0, *install_dir, basename); *install_loc = file_name_in_vicinity (0, ".", basename); } else { t_uchar * relpath = 0; t_uchar * install_loc_dir = 0; while (str_cmp (loc_dir, ".")) { t_uchar * dir_id = 0; /* not allocated */ t_uchar * loc_dir_in_tgt = 0; /* not allocated */ t_uchar * dir_tail = 0; t_uchar * s; dir_id = assoc_ref (mod_dir_id_of, loc_dir); if (!dir_id) { /* A degenerate changeset -- it should include that dir-id but * doesn't. Let's try a guess. */ dir_id = assoc_ref (running->dir_id_of, loc_dir); } loc_dir_in_tgt = assoc_ref (running->dir_loc_of, dir_id); if (loc_dir_in_tgt) break; dir_tail = file_name_tail (0, loc_dir); if (!relpath) relpath = str_save (0, dir_tail); else { t_uchar * t = relpath; relpath = file_name_in_vicinity (0, dir_tail, relpath); lim_free (0, t); } s = file_name_directory_file (0, loc_dir); lim_free (0, loc_dir); loc_dir = s; lim_free (0, dir_tail); } *target_has_dir = str_save (0, loc_dir); install_loc_dir = file_name_in_vicinity (0, loc_dir, relpath); if (!str_cmp (install_loc_dir, "./")) *install_dir = str_save (0, target); else *install_dir = file_name_in_vicinity (0, target, 2 + install_loc_dir); *install_name = file_name_in_vicinity (0, *install_dir, basename); *install_loc = file_name_in_vicinity (0, install_loc_dir, basename); lim_free (0, relpath); lim_free (0, install_loc_dir); } if (is_dir) { int x; if (!is_rename) { t_uchar *old_loc = assoc_ref (running->dir_loc_of, id); if (old_loc && str_cmp (old_loc, *install_loc)) { debug (dbg_apply, 6, "conflict installing directory id '%s', old path: '%s', new path '%s'\n", id, old_loc, *install_loc); result = -1; goto done; } } assoc_set (&running->dir_loc_of, id, *install_loc); assoc_set (&running->dir_id_of, *install_loc, id); for (x = 0; x < rel_n_records (file_set_aside_with); ++x) { int cmp; t_uchar * new_loc = 0; t_uchar * sub_id; cmp = str_cmp (file_set_aside_with[x][0], id); if (cmp < 0) continue; else if (cmp > 0) break; new_loc = file_name_in_vicinity (0, *install_loc, file_set_aside_with[x][1]); sub_id = file_set_aside_with[x][2]; assoc_set (&running->file_loc_of, sub_id, new_loc); assoc_set (&running->file_id_of, new_loc, sub_id); lim_free (0, new_loc); } for (x = 0; x < rel_n_records (dir_set_aside_with); ++x) { int cmp; t_uchar * new_loc = 0; t_uchar * sub_id; cmp = str_cmp (dir_set_aside_with[x][0], id); if (cmp < 0) continue; else if (cmp > 0) break; new_loc = file_name_in_vicinity (0, *install_loc, dir_set_aside_with[x][1]); sub_id = dir_set_aside_with[x][2]; assoc_set (&running->dir_loc_of, sub_id, new_loc); assoc_set (&running->dir_id_of, new_loc, sub_id); lim_free (0, new_loc); } } else { if (!is_rename) { t_uchar *old_loc = assoc_ref (running->file_loc_of, id); if (old_loc && str_cmp (old_loc, *install_loc)) { debug (dbg_apply, 6, "conflict installing file id '%s', old path: '%s', new path '%s'\n", id, old_loc, *install_loc); result = -1; goto done; } } assoc_set (&running->file_loc_of, id, *install_loc); assoc_set (&running->file_id_of, *install_loc, id); } done: lim_free (0, basename); lim_free (0, loc_dir); return result; } /** * \brief ensure a directory exists, turning fiels in its place into conflicts * NOTE: this appears to ignore the desired id of the dir */ static void ensure_directory_eliminating_conflicts (apply_changeset_t * apply, t_uchar * target_has_path, t_uchar * dir) { t_uchar * dir_of_dir = 0; if (!str_cmp (target_has_path, dir)) return; dir_of_dir = file_name_directory_file (0, dir); ensure_directory_eliminating_conflicts (apply, target_has_path, dir_of_dir); /* dir exists, we're done */ if (safe_file_is_directory (dir)) { lim_free (0, dir_of_dir); return; } /* directory path is a file - make it a conflict */ if (!safe_access (dir, F_OK)) { t_uchar * loc; t_uchar * orig; loc = str_alloc_cat (0, "./", dir + str_length (apply->target->root) + 1); conflict_filenames (apply->target->root, dir, &orig, NULL, &apply->running); deferred_conflict (apply, loc, 0, &apply->running); safe_rename (dir, orig); lim_free (0, loc); lim_free (0, orig); } safe_mkdir (dir, 0777); lim_free (0, dir_of_dir); } static int run_diff3 (t_uchar * basename, t_uchar * mine_path, t_uchar * older_path, t_uchar * yours_path) { int pid; pid = fork (); if (pid == -1) panic ("unable to fork for diff3"); if (pid) { int status; int wait_pid; wait_pid = waitpid (pid, &status, 0); if (wait_pid < 0) { panic_msg ("error waiting for patch subprocess"); kill (0, SIGKILL); panic ("error waiting for subprocess"); } if (WIFSIGNALED (status)) { safe_printfmt (2, "\n"); safe_printfmt (2, "arch_apply_changeset: diff3 subprocess killed by signal %d\n", WTERMSIG (status)); safe_printfmt (2, "\n"); exit (2); } else if (!WIFEXITED (status)) { panic_msg ("waitpid returned for a non-exited process"); kill (0, SIGKILL); panic ("waitpid returned for a non-exited process"); } else { int exit_status; exit_status = WEXITSTATUS (status); if (exit_status == 1) { t_uchar * rej_name = str_alloc_cat (0, basename, ".rej"); int fd; if (!safe_access (rej_name, F_OK)) { t_uchar * setaside_base_name = str_alloc_cat (0, ",,saved-", rej_name); safe_rename (rej_name, setaside_base_name); lim_free (0, setaside_base_name); } fd = safe_open (rej_name, O_WRONLY | O_CREAT | O_EXCL, 0444); safe_printfmt (fd, "Conflicts occurred, diff3 conflict markers left in file.\n"); safe_close (fd); lim_free (0, rej_name); } return exit_status; } } else { int output_redir_fd; int input_redir_fd; t_uchar ** argv; argv = 0; ar_push_uchar_star (&argv, cfg__gnu_diff3); ar_push_uchar_star (&argv, "-E"); ar_push_uchar_star (&argv, "--merge"); ar_push_uchar_star (&argv, "-L"); ar_push_uchar_star (&argv, "TREE"); ar_push_uchar_star (&argv, "-L"); ar_push_uchar_star (&argv, "ANCESTOR"); ar_push_uchar_star (&argv, "-L"); ar_push_uchar_star (&argv, "MERGE-SOURCE"); ar_push_uchar_star (&argv, mine_path); ar_push_uchar_star (&argv, older_path); ar_push_uchar_star (&argv, yours_path); ar_push_uchar_star (&argv, 0); input_redir_fd = safe_open ("/dev/null", O_RDONLY, 0); output_redir_fd = safe_open (basename, O_WRONLY | O_CREAT | O_EXCL, 0666); safe_move_fd (input_redir_fd, 0); safe_move_fd (output_redir_fd, 1); output_redir_fd = safe_dup (1); safe_move_fd (output_redir_fd, 2); arch_util_execvp (cfg__gnu_diff3, argv); panic ("arch_apply_changeset: execvp for diff3 returned to caller"); exit (2); } panic ("dopatch: not reached (run_diff3)"); return 2; } static int run_patch (int apply_in_reverse, int forward_opt_to_patch, char * patch_path, char * tree_file_base, char * original_inode_name) { int pid; pid = fork (); if (pid == -1) panic ("unable to fork for patch"); if (pid) { int status; int wait_pid; wait_pid = waitpid (pid, &status, 0); if (wait_pid < 0) { panic_msg ("error waiting for patch subprocess"); kill (0, SIGKILL); panic ("error waiting for subprocess"); } if (WIFSIGNALED (status)) { safe_printfmt (2, "\n"); safe_printfmt (2, "arch_apply_changeset: patch subprocess killed by signal %d\n", WTERMSIG (status)); safe_printfmt (2, "\n"); exit (2); } else if (!WIFEXITED (status)) { panic_msg ("waitpid returned for a non-exited process"); kill (0, SIGKILL); panic ("waitpid returned for a non-exited process"); } else { int exit_status; exit_status = WEXITSTATUS (status); return exit_status; } } else { int output_redir_fd; int input_redir_fd; t_uchar ** argv; argv = 0; ar_push_uchar_star (&argv, cfg__gnu_patch); if (forward_opt_to_patch) ar_push_uchar_star (&argv, "--forward"); if (apply_in_reverse) ar_push_uchar_star (&argv, "--reverse"); ar_push_uchar_star (&argv, "--binary"); ar_push_uchar_star (&argv, "-f"); ar_push_uchar_star (&argv, "-s"); ar_push_uchar_star (&argv, "--posix"); ar_push_uchar_star (&argv, "-i"); ar_push_uchar_star (&argv, patch_path); ar_push_uchar_star (&argv, "-o"); ar_push_uchar_star (&argv, tree_file_base); ar_push_uchar_star (&argv, original_inode_name); ar_push_uchar_star (&argv, 0); input_redir_fd = safe_open ("/dev/null", O_RDONLY, 0); output_redir_fd = safe_open (",,patch-output", O_WRONLY | O_CREAT | O_EXCL, 0666); safe_move_fd (input_redir_fd, 0); safe_move_fd (output_redir_fd, 1); output_redir_fd = safe_dup (1); safe_move_fd (output_redir_fd, 2); arch_util_execvp (cfg__gnu_patch, argv); panic ("arch_apply_changeset: execvp for patch returned to caller"); exit (2); } panic ("dopatch: not reached (run_patch)"); return 2; } void save_patch_for_missing_file (t_uchar * missing_patch_dir, t_uchar * patch_path, t_uchar * mod_loc) { t_uchar * mod_loc_dir = 0; t_uchar * dest_dir = 0; t_uchar * patch_tail = 0; t_uchar * dest_path = 0; mod_loc_dir = file_name_directory_file (0, mod_loc); dest_dir = file_name_in_vicinity (0, missing_patch_dir, mod_loc_dir); ensure_directory_exists (dest_dir); patch_tail = file_name_tail (0, patch_path); dest_path = file_name_in_vicinity (0, dest_dir, patch_tail); copy_file (patch_path, dest_path); lim_free (0, mod_loc_dir); lim_free (0, dest_dir); lim_free (0, patch_tail); lim_free (0, dest_path); } /** * \brief Rename files listed in removed_files into a temp dir * * The changeset_report callback is invoked for each file removed, and * associated data is updated. * \param tree The tree that the files are being removed from and the temp location will be rooted in. * \param removed_files The files to move to a temp dir * \param r The changeset_report to write to * \param running The data to update * \param escape_classes The set of character to pika-escape in output * \return The path to the temp directory */ static t_uchar * rename_removed_files(void * context, arch_project_tree_t * tree, apply_changeset_t * apply, struct running_inventory_assocs * running) { int x; t_uchar * tmp_removed_files_root = talloc_tmp_file_name (context, tree->root, ",,tmp-removed-files"); rmrf_file (tmp_removed_files_root); safe_mkdir (tmp_removed_files_root, 0777); for (x = 0; x < rel_n_records (apply->report->removed_files); ++x) { t_uchar * target_loc = 0; t_uchar * target_id = 0; t_uchar * target_path = 0; t_uchar * dest_path = 0; t_uchar * dest_dir = 0; target_loc = apply->report->removed_files[x][0]; target_id = apply->report->removed_files[x][1]; target_path = file_name_in_vicinity (0, tree->root, target_loc); dest_path = file_name_in_vicinity (0, tmp_removed_files_root, target_loc); dest_dir = file_name_directory_file (0, dest_path); one_path_callback (apply, "D %s\n", target_loc); ensure_directory_exists (dest_dir); safe_rename (target_path, dest_path); assoc_del (running->file_id_of, target_loc); assoc_del (running->file_loc_of, target_id); lim_free (0, target_path); lim_free (0, dest_path); lim_free (0, dest_dir); } return tmp_removed_files_root; } /** * \brief Generate a table listing id and name of all ids listed in source and * target * * Both tables must be sorted by id and have id at col 1, file location at col 0 * \param source A sorted table containing all ids to find * \param target A table containing a superset of all rows to print */ static rel_table find_target_equivalent (rel_table source, rel_table target) { rel_table table = rel_join (-1, rel_join_output (2,0, 2,1, -1), 1, 1, source, target); rel_sort_table_by_field (0, table, 0); return table; } /** * \brief Generate a table listing id and name of all ids listed in source but * not in target * * Both tables must be sorted by id and have id at col 1, file path at col 0 * The returned table will list * \param source A sorted table containing all ids to find * \param target A table to compare to source * \return a table with id at col 0, col 1 */ static rel_table find_target_missing(rel_table source, rel_table target) { rel_table table = rel_join (1, rel_join_output (1,0, 1,1, -1), 1, 1, source, target); rel_sort_table_by_field (0, table, 0); return table; } /** * set *orig and *reject to appropriate paths, * using ,.orig if .orig is in the inventory. */ void conflict_filenames(t_uchar const * const output_root, t_uchar const *full_path, t_uchar **orig, t_uchar **reject, struct running_inventory_assocs *inventory) { /* this may be a little inefficient, TODO optimise inventory mgmt */ int use_alternate = 0; if (inventory) { /* this is 'basename foo + .orig' */ t_uchar *tmp_orig = str_alloc_cat(0, full_path + 1 + str_length (output_root), ".orig"); if (assoc_ref (inventory->dir_id_of, tmp_orig)) use_alternate = -1; else if (assoc_ref (inventory->file_id_of, tmp_orig)) use_alternate = -1; lim_free (0, tmp_orig); } if (use_alternate) { if (orig) *orig = str_alloc_cat (0, full_path, ",.orig"); if (reject) *reject = str_alloc_cat (0, full_path, ",.rej"); } else { if (orig) *orig = str_alloc_cat (0, full_path, ".orig"); if (reject) *reject = str_alloc_cat (0, full_path, ".rej"); } } void apply_print_callback (void * vfd, char * fmt, va_list ap) { int fd; fd = (int)(t_ulong)vfd; safe_printfmt_va_list (fd, fmt, ap); safe_flush (fd); } /** * \brief identify removed and missing removed files * \param changeset the changeset we are applying * \param inventory the inventory of the changeset * \param r the application report we are creating */ void identify_removed_and_missing_removed_files (struct arch_changeset_report * changeset, struct arch_changeset_inventory * inventory, struct arch_apply_changeset_report * r) { rel_table removed_files_index = rel_copy_table (changeset->removed_files); rel_append_x (&removed_files_index, changeset->removed_symlinks); rel_sort_table_by_field (0, removed_files_index, 1); r->removed_files = find_target_equivalent (removed_files_index, inventory->files); r->missing_removed_files = find_target_missing(removed_files_index, inventory->files); rel_free_table (removed_files_index); } /** * \brief prety up the report for nice iteration etc * \param report the report */ void report_sort_and_unique (struct arch_apply_changeset_report * r) { /**************************************************************** * Sort and Uniq Report Fields */ rel_sort_table_by_field (0, r->removed_files, 0); rel_uniq_by_field (&r->removed_files, 0); talloc_steal (r, ar_base (r->removed_files)); rel_sort_table_by_field (0, r->removed_dirs, 0); rel_uniq_by_field (&r->removed_dirs, 0); rel_sort_table_by_field (0, r->missing_removed_files, 0); rel_uniq_by_field (&r->missing_removed_files, 0); rel_sort_table_by_field (0, r->missing_removed_dirs, 0); rel_uniq_by_field (&r->missing_removed_dirs, 0); rel_sort_table_by_field (0, r->missing_renamed_files, 0); rel_uniq_by_field (&r->missing_renamed_files, 0); rel_sort_table_by_field (0, r->missing_renamed_dirs, 0); rel_uniq_by_field (&r->missing_renamed_dirs, 0); rel_sort_table_by_field (0, r->new_dirs, 0); rel_uniq_by_field (&r->new_dirs, 0); rel_sort_table_by_field (0, r->renamed_dirs, 0); rel_uniq_by_field (&r->renamed_dirs, 0); rel_sort_table_by_field (0, r->new_files, 0); rel_uniq_by_field (&r->new_files, 0); rel_sort_table_by_field (0, r->renamed_files, 0); rel_uniq_by_field (&r->renamed_files, 0); rel_sort_table_by_field (0, r->modified_files, 0); rel_uniq_by_field (&r->modified_files, 0); rel_sort_table_by_field (0, r->modified_dirs, 0); rel_uniq_by_field (&r->modified_dirs, 0); rel_sort_table_by_field (0, r->missing_file_for_patch, 0); rel_uniq_by_field (&r->missing_file_for_patch, 0); rel_sort_table_by_field (0, r->missing_dir_for_patch, 0); rel_uniq_by_field (&r->missing_dir_for_patch, 0); rel_sort_table_by_field (0, r->meta_modified_files, 0); rel_uniq_by_field (&r->meta_modified_files, 0); rel_sort_table_by_field (0, r->meta_modified_dirs, 0); rel_uniq_by_field (&r->meta_modified_dirs, 0); rel_sort_table_by_field (0, r->missing_file_for_meta_patch, 0); rel_uniq_by_field (&r->missing_file_for_meta_patch, 0); rel_sort_table_by_field (0, r->missing_dir_for_meta_patch, 0); rel_uniq_by_field (&r->missing_dir_for_meta_patch, 0); rel_sort_table_by_field (0, r->conflict_files, 0); rel_uniq_by_field (&r->conflict_files, 0); rel_sort_table_by_field (0, r->conflict_dirs, 0); rel_uniq_by_field (&r->conflict_dirs, 0); rel_sort_table_by_field (0, r->metadata_conflict_files, 0); rel_uniq_by_field (&r->metadata_conflict_files, 0); rel_sort_table_by_field (0, r->metadata_conflict_dirs, 0); rel_uniq_by_field (&r->metadata_conflict_dirs, 0); } void read_changeset (apply_changeset_t * apply, t_uchar const * changeset_spec) { t_uchar * changeset_path; safe_chdir (changeset_spec); changeset_path = safe_current_working_directory (); safe_fchdir (apply->here_fd); arch_changeset_report_init (&apply->changeset); arch_evaluate_changeset (&apply->changeset, changeset_path); lim_free (0, changeset_path); } void setup_running (apply_changeset_t * apply, struct arch_changeset_inventory * inventory) { /**************************************************************** * Build assoc tables of the inventory. * * These will be kept up-to-date as files and dirs get * deleted, added, and renamed. */ apply->running.dir_loc_of = rel_to_assoc (inventory->dirs, 1, 0); apply->running.dir_id_of = rel_to_assoc (inventory->dirs, 0, 1); apply->running.file_loc_of = rel_to_assoc (inventory->files, 1, 0); apply->running.file_id_of = rel_to_assoc (inventory->files, 0, 1); assoc_set (&apply->running.dir_id_of, ".", "?_."); assoc_set (&apply->running.dir_loc_of, "?_.", "."); } /* tag: Tom Lord Thu May 15 17:19:28 2003 (apply-changeset.c) */