/* 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)
*/
syntax highlighted by Code2HTML, v. 0.9.1