summaryrefslogtreecommitdiff
path: root/src/commit.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/commit.c')
-rw-r--r--src/commit.c1364
1 files changed, 1364 insertions, 0 deletions
diff --git a/src/commit.c b/src/commit.c
new file mode 100644
index 0000000..a136b58
--- /dev/null
+++ b/src/commit.c
@@ -0,0 +1,1364 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <limits.h>
+
+#include "cli_val.h"
+#include "cli_objects.h"
+#include "cli_parse.h"
+#include "cli_path_utils.h"
+
+static char def_name[] = DEF_NAME;
+static char tag_name[] = TAG_NAME;
+static char opaque_name[] = OPQ_NAME;
+
+static int fin_commit(boolean ok);
+static boolean commit_value(vtw_def *defp, char *cp,
+ vtw_cmode mode, boolean in_txn);
+static void perform_create_node();
+static void perform_delete_node();
+static void perform_move();
+static boolean commit_delete_child(vtw_def *pdefp, char *child,
+ boolean deleting, boolean in_txn);
+static boolean commit_delete_children(vtw_def *defp, boolean deleting,
+ boolean in_txn);
+static boolean commit_update_children(vtw_def *defp, boolean creating,
+ boolean in_txn, boolean *parent_update);
+
+#if BITWISE
+static void make_dir()
+{
+ struct stat statbuf;
+ if (lstat(m_path.path, &statbuf) < 0) {
+ char *command;
+ command = my_malloc(strlen(m_path.path) + 10, "set");
+ sprintf(command, "mkdir -p %s", m_path.path);
+ system(command);
+ free(command);
+ return;
+ }
+ if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
+ bye("directory %s expected, found regular file", m_path.path);
+ }
+ return;
+}
+#endif
+
+/*************************************************
+ validate_dir_for_commit:
+ validate value.value if there is one, validate
+ subdirectries names (for tag directory, and for
+ regular directory);
+ validate subdirectories
+ returns TRUE if OK, FASLE if errors
+ exits with status != 0 in case of parse error
+*/
+static boolean validate_dir_for_commit()
+{
+ struct stat statbuf;
+ int status=0;
+ vtw_def def;
+ boolean def_present=FALSE;
+ boolean value_present=FALSE;
+ int subdirs_number=0;
+ DIR *dp=NULL;
+ struct dirent *dirp=NULL;
+ char *cp=NULL;
+ boolean ret=TRUE;
+ char *uename = NULL;
+
+#ifdef DEBUG
+ printf("validating directory (node_cnt %d, free_node_cnt %d)\n"
+ "t_path |%s|\n",
+ node_cnt, free_node_cnt, t_path.path);
+#endif
+
+ /* find definition */
+
+ push_path(&t_path, def_name); /* PUSH 1 */
+ if ((lstat(t_path.path, &statbuf) >= 0) &&
+ ((statbuf.st_mode & S_IFMT) == S_IFREG)) {
+ /* definition present */
+ def_present = TRUE;
+ memset(&def, 0, sizeof(def));
+#ifdef DEBUG1
+ printf("Parsing definition\n");
+#endif
+
+ if ((status = parse_def(&def, t_path.path,
+ FALSE))) {
+ exit(status);
+ }
+
+ }
+#ifdef DEBUG1
+ else
+ printf("No definition\n");
+#endif
+ pop_path(&t_path); /* for PUSH 1 */
+
+ /* look at modified stuff */
+ if ((dp = opendir(m_path.path)) == NULL){
+ INTERNAL;
+ }
+ if (def_present && def.tag) {
+ push_path(&t_path, tag_name); /* PUSH 2a */
+ }
+
+ while ((dirp = readdir(dp)) != NULL) {
+
+ if (strcmp(dirp->d_name, ".") == 0 ||
+ strcmp(dirp->d_name, "..") == 0 ||
+ strcmp(dirp->d_name, MOD_NAME) == 0 ||
+ strcmp(dirp->d_name, LOCK_NAME) == 0 ||
+ strcmp(dirp->d_name, opaque_name) == 0 ||
+ strncmp(dirp->d_name, ".wh.", 4) == 0) {
+ continue; /*ignore dot and dot-dot*/
+ }
+
+ subdirs_number++;
+
+ if(uename)
+ my_free(uename);
+
+ uename = clind_unescape(dirp->d_name);
+
+ if (strcmp(uename, VAL_NAME) == 0) {
+
+ value_present=TRUE;
+
+ /* deal with the value */
+ if (!def_present) {
+ printf("There is no definition specified in template\n"
+ "\t%s\n\t - therefore no value permitted\n",
+ t_path.path);
+ ret = FALSE;
+ continue;
+ }
+
+ if (def.tag) {
+ printf("Tag specified in template\n"
+ "\t%s\n\t - therefore no value permitted\n",
+ t_path.path);
+ ret = FALSE;
+ continue;
+ }
+
+ /* value is OK */
+ /* read it */
+ cp = NULL;
+ status = get_value(&cp, &m_path);
+ if (status == VTWERR_OK){
+#ifdef DEBUG1
+ printf("Validating value |%s|\n"
+ "for path %s\n", cp, m_path.path);
+#endif
+ status = validate_value(&def, cp);
+ ret = ret && status;
+ }
+ if (cp)
+ my_free(cp);
+ continue;
+ }
+
+ push_path(&m_path, uename); /* PUSH 3 */
+ if (lstat(m_path.path, &statbuf) < 0) {
+ printf("Can't read directory %s\n",
+ m_path.path);
+ ret = FALSE;
+ pop_path(&m_path); /* for PUSH 3 */
+ continue;
+ }
+
+ if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
+ printf("Non directory file %s\n", m_path.path);
+ ret = FALSE;
+ pop_path(&m_path); /* for PUSH 3 */
+ continue;
+ }
+
+ if (def_present && def.tag) {
+ /* do not push t_path, it is already pushed above */
+ /* validate dir name against definition */
+ boolean res = validate_value(&def, uename);
+ value_present=TRUE;
+ if (!res) {
+ ret = FALSE;
+ /* do not go inside bad directory */
+ pop_path(&m_path); /* for PUSH 3 */
+ continue;
+ }
+ } else {
+ push_path(&t_path, uename); /* PUSH 2b the same as PUSH 2a */
+ if (lstat(t_path.path, &statbuf) < 0) {
+ printf("No such template directory (%s)\n"
+ "for directory %s",
+ t_path.path, m_path.path);
+ ret = FALSE;
+ pop_path(&t_path); /* for PUSH 2b */
+ pop_path(&m_path); /* for PUSH 3 */
+ continue;
+ }
+ if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
+ printf("Non directory file %s\n", m_path.path);
+ ret = FALSE;
+ pop_path(&t_path); /* for PUSH 2b */
+ pop_path(&m_path); /* for PUSH 3 */
+ continue;
+ }
+ }
+
+ status = validate_dir_for_commit();
+ ret = ret && status;
+ pop_path(&m_path); /* for PUSH 3 */
+ if (!def_present || !def.tag)
+ pop_path(&t_path); /* for PUSH 2b */
+
+ }
+ status = closedir(dp);
+ if (status)
+ bye("Cannot close dir %s\n", m_path.path);
+
+ if(!value_present && def_present && !def.tag) {
+ ret = ret && validate_value(&def, "");
+ }
+
+ if (def_present && def.tag)
+ pop_path(&t_path); /* for PUSH 3a */
+ if (def_present)
+ free_def(&def);
+#ifdef DEBUG
+ printf("directory done(node_cnt %d, free_node_cnt %d)\n"
+ "mpath |%s|\n",
+ node_cnt, free_node_cnt, t_path.path);
+#endif
+ if (uename)
+ my_free(uename);
+ return ret;
+}
+
+/***************************************************
+ main:
+ main function (for now)
+***************************************************/
+
+int main(int argc, char **argv)
+{
+
+ boolean status;
+ char *mod;
+ struct stat statbuf;
+ int st;
+ boolean update_pending = FALSE;
+
+ set_in_commit(TRUE);
+ dump_log( argc, argv);
+ init_paths(TRUE);
+ mod = my_malloc(strlen(get_mdirp()) + strlen(MOD_NAME)+2, "COMMIT");
+ sprintf(mod, "%s/%s", get_mdirp(), MOD_NAME);
+ st = lstat(mod, &statbuf);
+ my_free(mod);
+ if (st < 0 ) {
+ bye("No configuration changes to commit\n");
+ exit(-1);
+ }
+
+ if(!get_config_lock(get_adirp(), LOCK_NAME)) {
+ bye("Configuration is locked\n");
+ }
+
+ status = validate_dir_for_commit();
+ if (status == TRUE) {
+ switch_path(CPATH);
+ status = commit_delete_children(NULL, FALSE, FALSE);
+ }
+ if (status == TRUE)
+ status = commit_update_children(NULL, FALSE, FALSE, &update_pending);
+ fin_commit(status);
+
+ done();
+
+ return (status == TRUE) ? 0 : 1;
+}
+
+/*************************************************************
+ perform_create_node -
+ remove node and descendent from woring path
+ and create a new node
+*************************************************************/
+static void perform_create_node()
+{
+#if BITWISE
+ static char format[]="rm -f -r %s;mkdir %s";
+ char *command;
+ switch_path(APATH);
+ command = my_malloc(2 * strlen(m_path.path) + sizeof(format),
+ "commit_create");
+ sprintf(command, format, m_path.path, m_path.path);
+ system(command);
+ my_free(command);
+ switch_path(CPATH);
+#endif
+ return;
+}
+
+
+/*************************************************************
+ perform_delete_node -
+ delete node in current path
+*************************************************************/
+static void perform_delete_node()
+{
+#if BITWISE
+ static char format[]="rm -f -r %s";
+ char *command;
+ command = my_malloc(strlen(m_path.path) + sizeof(format),
+ "commit_delete");
+ sprintf(command, format, m_path.path);
+ system(command);
+ my_free(command);
+#endif
+ return;
+}
+static void perform_move()
+{
+#if BITWISE
+ static char format[] = "rm -r -f %s;mkdir %s;mv %s/" VAL_NAME " %s";
+ char *a_path;
+ char *command;
+ switch_path(APATH);
+ a_path = my_strdup(m_path.path, "");
+ switch_path(CPATH);
+ command = my_malloc(sizeof(format)+3*strlen(a_path)+strlen(m_path.path),"");
+ sprintf(command, format, a_path, a_path, m_path.path, a_path);
+ system(command);
+ my_free(command);
+ my_free(a_path);
+#endif
+ return;
+}
+
+/*************************************************
+ commit_update_child:
+ preconditions: t_path and c_path set up for parent
+ pdefp (IN) - parent definition (may be NULL)
+ child (IN) - name of the child
+ creating (IN) - mode of commiting (update or create)
+ update_parent (OUT) - unfulfilled update request
+ commit child and its descendants
+ returns TRUE if no errors
+ exit if error during execution
+*/
+boolean commit_update_child(vtw_def *pdefp, char *child,
+ boolean creating, boolean in_txn, boolean *update_parent)
+{
+ boolean update_pending = FALSE;
+ boolean multi_tag = FALSE;
+ struct stat statbuf;
+ int status;
+ vtw_def def;
+ vtw_def *my_defp; /*def to be given to my children */
+ vtw_def *act_defp;/*def to be used for actions */
+ char *cp;
+ vtw_mark mark;
+ vtw_act_type act;
+ boolean do_end, ok, do_begin = FALSE, do_txn = FALSE;
+
+ set_at_string(NULL);
+
+ ok = TRUE;
+#ifdef DEBUG
+ printf("commiting directory (node_cnt %d, free_node_cnt %d)\n"
+ "mpath |%s|\n",
+ node_cnt, free_node_cnt, t_path.path);
+#endif
+ if (strcmp(child, ".") == 0 ||
+ strcmp(child, "..") == 0 ||
+ strcmp(child, MOD_NAME) == 0 ||
+ strcmp(child, LOCK_NAME) == 0 ||
+ strcmp(child, opaque_name) == 0)
+ return TRUE;
+
+ /* deleted subdirectory */
+ if (strncmp(child, ".wh.", 4) == 0) { /* whiteout */
+ /* ignore */
+ return TRUE;
+ }
+ mark_paths(&mark);
+ if (!creating) {
+ /* are we marked with opaque */
+ push_path(&m_path, child);
+ push_path(&m_path, opaque_name);
+ if (lstat(m_path.path, &statbuf) >= 0 && !creating) {
+ creating = TRUE;
+ }
+ pop_path(&m_path);
+ pop_path(&m_path);
+ }
+ /* find our definition */
+ if (pdefp && pdefp->tag) {
+ my_defp = NULL;
+ act_defp = pdefp;
+ push_path(&t_path,tag_name);
+ } else {
+ push_path(&t_path, child);
+ push_path(&t_path, def_name);
+ if ((lstat(t_path.path, &statbuf) >= 0) &&
+ ((statbuf.st_mode & S_IFMT) == S_IFREG)) {
+ /* defniition present */
+ act_defp = my_defp = &def;
+ memset(&def, 0, sizeof(def));
+#ifdef DEBUG1
+ printf("Parsing definition\n");
+#endif
+ if ((status = parse_def(&def, t_path.path,
+ FALSE)))
+ exit(status);
+ my_defp = act_defp = &def;
+ if (def.tag)
+ multi_tag = TRUE;
+ } else {
+ my_defp = act_defp = NULL;
+ }
+ pop_path(&t_path); /*def_name */
+ }
+ do_end = FALSE;
+ if (!multi_tag && !in_txn && act_defp &&
+ (act_defp->actions[begin_act].vtw_list_head ||
+ act_defp->actions[end_act].vtw_list_head)){
+ /* we are traversing change directory
+ if we are here, there is a change somewhere */
+ do_txn = in_txn = TRUE;
+ if (act_defp->actions[end_act].vtw_list_head)
+ do_end = TRUE;
+ /* if creating, delete skipped this directory,
+ we have to do begin act */
+ if (creating &&
+ act_defp->actions[begin_act].vtw_list_head)
+ do_begin = TRUE;
+ }
+ push_path(&m_path, child);
+ /* are we a "value" parent node */
+ if (act_defp && !act_defp->tag && act_defp->def_type != ERROR_TYPE ) {
+ /* we are value node */
+ if (!act_defp->multi) {
+ /* single node */
+ /* create_mode do create,
+ update_mode do create or update */
+
+ if (creating)
+ act = create_act;
+ else
+ act = update_act;
+
+ if ((act==update_act) && !act_defp->actions[update_act].vtw_list_head){
+ /* updating but no action
+ ask parent to do - propagate up */
+ *update_parent = TRUE;
+ }
+ /* look for actions */
+ /* if act != create_act => run actions[act] if not empty */
+ /* if act == create_act
+ * if actions[create_act] not empty
+ * run it
+ * else
+ * run actions[update_act] if not empty
+ * run actions[activate_act] if not empty
+ */
+ if (act_defp->actions[act].vtw_list_head
+ || (act == create_act
+ && (act_defp->actions[activate_act].vtw_list_head
+ || act_defp->actions[update_act].vtw_list_head))) {
+ status = get_value_to_at_string(&m_path);
+ if (status != VTWERR_OK)
+ bye("Can not read value at %s", m_path.path);
+ /* remove \n at the line end */
+ cp = strchr(get_at_string(), '\n');
+ if (cp)
+ *cp = 0;
+ if (act_defp->actions[act].vtw_list_head) {
+ status = execute_list(act_defp->actions[act].vtw_list_head,
+ act_defp);
+ if (!status) {
+#ifdef DEBUG
+ bye("begin action not NULL for %s\n", get_at_string());
+#else
+ return(FALSE);
+#endif
+ }
+ } else {
+ /* creating but no create action */
+ /* try update action */
+ if ((act == create_act)
+ && act_defp->actions[update_act].vtw_list_head) {
+ status
+ = execute_list(act_defp->actions[update_act].vtw_list_head,
+ act_defp);
+ if (!status) {
+ return (FALSE);
+ }
+ }
+ }
+ /* try activate action if creating */
+ if ((act == create_act)
+ && act_defp->actions[activate_act].vtw_list_head) {
+ status
+ = execute_list(act_defp->actions[activate_act].vtw_list_head,
+ act_defp);
+ if (!status) {
+ return (FALSE);
+ }
+ }
+ free_at_string();
+ }
+ /* now handle commit value */
+ if (!in_txn) { /* ELSE WAIT TILL THE END OF TXN */
+ perform_move();
+ perform_delete_node();
+ }
+ goto restore;
+ }
+ /* else multi_node */
+ cp = NULL;
+ status = get_value(&cp, &m_path);
+ if (status != VTWERR_OK)
+ bye("Can not read value at %s", m_path.path);
+ ok = commit_value(act_defp, cp, creating?create_mode:update_mode, in_txn);
+ if (cp)
+ my_free(cp);
+ goto restore;
+ }
+ /* else not a value */
+ /* regular */
+ /* do not do anything for tag type multinode */
+ if (!multi_tag && creating) {
+ set_at_string(child); /* for expand inside actions */
+ if (do_begin) {
+ status = execute_list(act_defp->
+ actions[begin_act].
+ vtw_list_head, act_defp);
+ if (!status) {
+#ifdef DEBUG
+ bye("begin action not NULL for %s\n", get_at_string());
+#else
+ return(FALSE);
+#endif
+ }
+ }
+
+ if (act_defp) {
+ if (act_defp->actions[create_act].vtw_list_head) {
+ status
+ = execute_list(act_defp->actions[create_act].vtw_list_head,
+ act_defp);
+ if (!status) {
+ return (FALSE);
+ }
+ } else if (act_defp->actions[update_act].vtw_list_head) {
+ /* no create action => use update action */
+ status
+ = execute_list(act_defp->actions[update_act].vtw_list_head,
+ act_defp);
+ if (!status) {
+ return (FALSE);
+ }
+ }
+ /* not trying activate action here (activate after children are
+ * configured)
+ */
+ }
+ }
+ if (creating && !in_txn) /* ELSE WAIT TILL THE END OF TXN */
+ perform_create_node();
+ /* children */
+ ok = commit_update_children(my_defp, creating, in_txn, &update_pending);
+ if (!ok)
+ return(FALSE);
+
+ if (update_pending){
+ if (!multi_tag && act_defp &&
+ act_defp->actions[update_act].vtw_list_head){
+ set_at_string(child); /* for expand inside actions */
+ ok = execute_list(act_defp->actions[update_act].
+ vtw_list_head, act_defp);
+ if (!ok)
+ return(FALSE);
+ /* update_pending = FALSE; */
+ } else
+ *update_parent = TRUE;
+ }
+ if (creating && !multi_tag && act_defp &&
+ act_defp->actions[activate_act].vtw_list_head){
+ set_at_string(child); /* for expand inside actions */
+ ok = execute_list(act_defp->actions[activate_act].
+ vtw_list_head, act_defp);
+ /* ignore result */
+ }
+ if (!in_txn) /* ELSE WAIT TILL THE END OF TXN */
+ perform_delete_node();
+ restore:
+ if (do_end){
+ set_at_string(child);
+ ok = execute_list(act_defp->actions[end_act].
+ vtw_list_head, act_defp);
+ }
+#if BITWISE
+ if (do_txn && ok) {
+ int len;
+ char *command;
+ char format1[]="rm -r -f %s/*;cp -r -f %s/%s %s";
+ char format2[]="rm -r -f %s/%s;mv %s/%s %s";
+ char format3[]="rm -r -f %s/%s";
+ restore_paths(&mark);
+ switch_path(MPATH);
+ len = sizeof(format1) + 2 * strlen(get_tmpp()) +
+ strlen(m_path.path) + strlen(child);
+ command = my_malloc(len, "");
+ sprintf(command, format1, get_tmpp(), m_path.path, child,
+ get_tmpp());
+ system(command);
+ my_free(command);
+ switch_path(APATH);
+ len = sizeof(format2) + 2 *strlen(m_path.path) +
+ 2 * strlen( child) + strlen(get_tmpp());
+ command = my_malloc(len, "");
+ sprintf(command, format2, m_path.path, child, get_tmpp(),
+ child, m_path.path);
+ system(command);
+ my_free(command);
+ switch_path(CPATH);
+ len = sizeof(format3) + strlen(m_path.path) +
+ strlen( child);
+ command = my_malloc(len, "");
+ sprintf(command, format3, m_path.path, child);
+ system(command);
+ my_free(command);
+ }
+#endif
+ restore_paths(&mark);
+ if (my_defp && my_defp != pdefp)
+ free_def(my_defp);
+ return ok;
+}
+
+
+/*************************************************
+ commit_delete_child:
+ preconditions: t_path and c_path set up for parent
+ pdefp (IN) - parent definition (may be NULL)
+ child (IN) - name of the child
+ do_del (IN) - if FALSE we are looking for delete target
+ if TRUE, we found and switched to A (working) PATH
+ commit deleted child and its descendants
+ returns TRUE if no errors
+ exit if error during execution
+*/
+static boolean commit_delete_child(vtw_def *pdefp, char *child,
+ boolean deleting, boolean in_txn)
+{
+ boolean do_children, multi_tag = FALSE;
+ struct stat statbuf;
+ int status;
+ vtw_def def;
+ vtw_def *my_defp; /*def to be given to my children */
+ vtw_def *act_defp;/*def to be used for actions */
+ char *cp;
+ vtw_mark mark;
+ boolean ok, do_txn = FALSE;
+ int st;
+ ok = TRUE;
+
+#ifdef DEBUG
+ printf("commiting directory (node_cnt %d, free_node_cnt %d)\n"
+ "mpath |%s|\n",
+ node_cnt, free_node_cnt, t_path.path);
+#endif
+
+ /* deleted subdirectory */
+ if (strncmp(child, ".wh.", 4) == 0) { /* whiteout */
+ if (deleting)
+ /* in do_delete mode we traverse A hierarchy, no white-outs possible */
+ INTERNAL; /* it is exit */
+ else {
+ /* deal with counterpart in working */
+ switch_path(APATH);
+ push_path(&m_path, child+4);
+ st = lstat(m_path.path, &statbuf);
+ pop_path(&m_path);
+ if (st >= 0){
+ /*get rid of ".wh. part in child name"*/
+ /* deleting mode will handle txn, both
+ begin and end
+ */
+ ok = commit_delete_child(pdefp, child + 4, TRUE, in_txn);
+ }else {
+ /* I do not understand how we could be here */
+ printf("Mystery #1\n");
+ ok = TRUE;
+ }
+ switch_path(CPATH);
+ if (ok) {
+ /* delete whiteout */
+ if (!in_txn){ /*ELSE WAIT TILL THE END OF TXN*/
+ push_path(&m_path, child);
+ perform_delete_node();
+ pop_path(&m_path);
+ }
+ }
+ return ok;
+ }
+ /* done with whiteouts */
+ }
+ /* not white out */
+ mark_paths(&mark);
+ if (!deleting) {
+ int status;
+ /* are we marked with opaque */
+ push_path(&m_path, child);
+ push_path(&m_path, opaque_name);
+ status = lstat(m_path.path, &statbuf);
+ pop_path(&m_path);
+ pop_path(&m_path);
+ if (status >= 0) {
+ /* brand new directory, nothing is
+ deleted there;
+ update will handle txn (both begin and end)
+ */
+ return TRUE;
+ }
+ }
+ /* find our definition */
+ if (pdefp && pdefp->tag) {
+ /* parent is a tag, node is a tag value node */
+ my_defp = NULL;
+ act_defp = pdefp;
+ push_path(&t_path,tag_name);
+ } else {
+ push_path(&t_path, child);
+ push_path(&t_path, def_name);
+ if ((lstat(t_path.path, &statbuf) >= 0) &&
+ ((statbuf.st_mode & S_IFMT) == S_IFREG)) {
+ /* defniition present */
+ act_defp = my_defp = &def;
+ memset(&def, 0, sizeof(def));
+#ifdef DEBUG1
+ printf("Parsing definition\n");
+#endif
+ if ((status = parse_def(&def, t_path.path,
+ FALSE)))
+ exit(status);
+ my_defp = act_defp = &def;
+ if (def.tag)
+ multi_tag = TRUE; /* tag node itself*/
+ } else {
+ my_defp = act_defp = NULL;
+ }
+ pop_path(&t_path); /*def_name */
+ }
+ push_path(&m_path, child);
+ if (!multi_tag) {
+ set_at_string(child); /* for expand inside actions */
+ /* deal with txn */
+ if (!in_txn && act_defp &&
+ (act_defp->actions[begin_act].vtw_list_head ||
+ act_defp->actions[end_act].vtw_list_head)) {
+ /* if we are here we have change,
+ we either in do_del and our node is change node
+ or we are in change directory and our node is
+ change node also */
+ if (deleting)
+ /* if not deleting, update will handle values */
+ do_txn = TRUE;
+ in_txn = TRUE;
+ if (act_defp->actions[begin_act].vtw_list_head){
+ status = execute_list(act_defp->actions[begin_act].
+ vtw_list_head, act_defp);
+ if (!status) {
+#ifdef DEBUG
+ bye("begin action not NULL for %s\n", get_at_string());
+#else
+ return(FALSE);
+#endif
+ }
+ }
+ }
+ }
+ /* are we a "value" parent node */
+ if (act_defp && !act_defp->tag && act_defp->def_type != ERROR_TYPE ) {
+ /* we are value node */
+ if (!act_defp->multi) {
+ /* single node */
+ if (!deleting) {
+ /* if it was whiteout, it was converted to do_del_mode */
+ restore_paths(&mark);
+ return ok;
+ }
+
+ /* do we have actions */
+ if (act_defp->actions[delete_act].vtw_list_head){
+ status = get_value_to_at_string(&m_path);
+ if (status != VTWERR_OK)
+ bye("Can not read value at %s", m_path.path);
+ /* remove \n at the line end */
+ cp = strchr(get_at_string(), '\n');
+ if (cp)
+ *cp = 0;
+ if (act_defp->actions[delete_act].vtw_list_head) {
+ set_in_delete_action(TRUE);
+ status = execute_list(act_defp->actions[delete_act].
+ vtw_list_head, act_defp);
+ set_in_delete_action(FALSE);
+ if (!status) {
+#ifdef DEBUG
+ bye("begin action not NULL for %s\n", get_at_string());
+#else
+ return(FALSE);
+#endif
+ }
+ }
+ free_at_string();
+ }
+ /* now handle commit value */
+ if (!in_txn) /* ELSE WAIT TILL THE END OF TXN */
+ perform_delete_node();
+ goto restore;
+ }
+ /* else multi_node */
+ cp = NULL;
+ status = get_value(&cp, &m_path);
+ if (status != VTWERR_OK)
+ bye("Can not read value at %s", m_path.path);
+ ok = commit_value(act_defp, cp, deleting?do_del_mode:del_mode,in_txn);
+ if (cp)
+ my_free(cp);
+ goto restore;
+ }
+ /* else not a value */
+ /* regular */
+ do_children = TRUE;
+ /* do not do anything for tag itself, all action belong to values */
+ if (!multi_tag) {
+ set_at_string(child); /* for expand inside actions */
+ if (deleting) {
+ if (act_defp &&
+ act_defp->actions[delete_act].vtw_list_head){
+ do_children = FALSE;
+ set_in_delete_action(TRUE);
+ status = execute_list(act_defp->actions[delete_act].
+ vtw_list_head, act_defp);
+ set_in_delete_action(FALSE);
+ if (!status) {
+#ifdef DEBUG
+ bye("begin action not NULL for %s\n", get_at_string());
+#else
+ return(FALSE);
+#endif
+ }
+ }
+ }
+ }
+ /* children */
+ if (do_children){
+ ok = commit_delete_children(my_defp, deleting, in_txn);
+ if (!ok)
+ goto restore;
+ }
+ if (deleting) {
+ if (do_txn && act_defp &&
+ act_defp->actions[end_act].vtw_list_head) {
+ set_at_string(child);
+ ok = execute_list(act_defp->actions[end_act].
+ vtw_list_head, act_defp);
+ if (!ok)
+ goto restore;
+ }
+ /* delete node and all its descendants */
+ if (!in_txn || do_txn)/* ELSE WAIT TILL THE END OF TXN */
+ perform_delete_node();
+ }
+ restore:
+ restore_paths(&mark);
+ if (my_defp && my_defp != pdefp)
+ free_def(my_defp);
+ return ok;
+}
+
+static boolean commit_delete_children(vtw_def *defp, boolean deleting,
+ boolean in_txn)
+{
+ DIR *dp;
+ int status;
+ struct dirent *dirp;
+ boolean ok = TRUE;
+ char *child;
+ vtw_type_e type;
+ valstruct mvals;
+ boolean first;
+ char *cp;
+ int elem, curi;
+ vtw_sorted cur_sorted;
+ char *uename = NULL;
+
+ if ((dp = opendir(m_path.path)) == NULL){
+ if (deleting)
+ return TRUE;
+ INTERNAL;
+ }
+ if (defp)
+ type = defp->def_type;
+ else
+ type = TEXT_TYPE;
+ if (type == ERROR_TYPE)
+ type = TEXT_TYPE;
+ first = TRUE;
+ memset(&mvals, 0, sizeof (valstruct));
+ memset(&cur_sorted, 0, sizeof(vtw_sorted));
+
+ while ((dirp = readdir(dp)) != NULL) {
+ child = dirp->d_name;
+ if (strcmp(child, ".") == 0 ||
+ strcmp(child, "..") == 0 ||
+ strcmp(child, MOD_NAME) == 0 ||
+ strcmp(child, LOCK_NAME) == 0 ||
+ strcmp(child, OPQ_NAME) == 0)
+ continue;
+ uename = clind_unescape(child);
+ cp = uename;
+ if (first) {
+ mvals.free_me = TRUE;
+ mvals.val = cp;
+ mvals.val_type = type;
+ first = FALSE;
+ } else {
+ if (mvals.cnt%MULTI_ALLOC == 0) {
+ /* convert into multivalue */
+ mvals.vals = my_realloc(mvals.vals,
+ (mvals.cnt + MULTI_ALLOC) *
+ sizeof(char *), "add_value");
+ if (mvals.cnt == 0) { /* single value - convert */
+ mvals.vals[0] = mvals.val;
+ mvals.cnt= 1;
+ mvals.val = NULL;
+ }
+ }
+ mvals.vals[mvals.cnt] = cp;
+ ++mvals.cnt;
+ }
+ }
+ status = closedir(dp);
+ if (status)
+ INTERNAL;
+ if (first) {
+ return TRUE;
+ }
+ vtw_sort(&mvals, &cur_sorted);
+ for (curi = 0; curi < cur_sorted.num && ok; ++curi){
+ if (type == TEXT_TYPE ||
+ type == BOOL_TYPE)
+ child = (char *)(cur_sorted.ptrs[curi]);
+ else {
+ elem = (((unsigned int *)(cur_sorted.ptrs[curi]))-
+ cur_sorted.parts)/
+ cur_sorted.partnum;
+ child = mvals.cnt?mvals.vals[elem]:
+ mvals.val;
+ }
+ ok = commit_delete_child(defp, child, deleting, in_txn);
+ }
+ free_val(&mvals);
+ free_sorted(&cur_sorted);
+ return ok;
+}
+
+static boolean commit_update_children(vtw_def *defp, boolean creating,
+ boolean in_txn, boolean *parent_update)
+{
+ DIR *dp;
+ int status;
+ struct dirent *dirp;
+ boolean ok = TRUE;
+ char *child;
+ vtw_type_e type;
+ valstruct mvals;
+ boolean first;
+ char *cp;
+ int elem, curi;
+ vtw_sorted cur_sorted;
+ char *uename = NULL;
+
+
+ if ((dp = opendir(m_path.path)) == NULL){
+ printf("%s:%d: opendir error: path=%s\n",
+ __FUNCTION__,__LINE__,m_path.path);
+ INTERNAL;
+ }
+
+ memset(&mvals, 0, sizeof (valstruct));
+ memset(&cur_sorted, 0, sizeof(vtw_sorted));
+ if (defp)
+ type = defp->def_type;
+ else
+ type = TEXT_TYPE;
+ if (type == ERROR_TYPE)
+ type = TEXT_TYPE;
+ first = TRUE;
+
+ while ((dirp = readdir(dp)) != NULL) {
+ child = dirp->d_name;
+ if (strcmp(child, ".") == 0 ||
+ strcmp(child, "..") == 0 ||
+ strcmp(child, MOD_NAME) == 0 ||
+ strcmp(child, LOCK_NAME) == 0 ||
+ strcmp(child, OPQ_NAME) == 0)
+ continue;
+ cp = uename = clind_unescape(child);
+ if (first) {
+ mvals.free_me = TRUE;
+ mvals.val = cp;
+ mvals.val_type = type;
+ first = FALSE;
+ } else {
+ if (mvals.cnt%MULTI_ALLOC == 0) {
+ /* convert into multivalue */
+ mvals.vals = my_realloc(mvals.vals,
+ (mvals.cnt + MULTI_ALLOC) *
+ sizeof(char *), "add_value");
+ if (mvals.cnt == 0) { /* single value - convert */
+ mvals.vals[0] = mvals.val;
+ mvals.cnt= 1;
+ mvals.val = NULL;
+ }
+ }
+ mvals.vals[mvals.cnt] = cp;
+ ++mvals.cnt;
+ }
+ }
+ status = closedir(dp);
+ if (status)
+ INTERNAL;
+ if (first) {
+ if (uename)
+ my_free(uename);
+ return TRUE;
+ }
+ vtw_sort(&mvals, &cur_sorted);
+ for (curi = 0; curi < cur_sorted.num && ok; ++curi){
+ if (type == TEXT_TYPE ||
+ type == BOOL_TYPE)
+ child = (char *)(cur_sorted.ptrs[curi]);
+ else {
+ elem = (((unsigned int *)(cur_sorted.ptrs[curi]))-
+ cur_sorted.parts)/
+ cur_sorted.partnum;
+ child = mvals.cnt?mvals.vals[elem]:
+ mvals.val;
+ }
+ ok = commit_update_child(defp, child, creating, in_txn, parent_update);
+ }
+ free_val(&mvals);
+ free_sorted(&cur_sorted);
+ return ok;
+}
+
+
+/*************************************************
+ commit_value:
+ executes commit for the value leave node
+**************************************************/
+static boolean commit_value(vtw_def *defp, char *cp,
+ vtw_cmode mode, boolean in_txn)
+{
+
+ valstruct act_value;
+ int status;
+ int curi,acti, partnum, res=0;
+ void *actp, *curp;
+ boolean no_shadow;
+ boolean ok;
+ int total, a_res, c_res;
+ char **a_ptr, **c_ptr, *val_string;
+ boolean cur_pr_val;
+ int pr_index;
+ int sign;
+ boolean creating;
+ vtw_node *actions;
+ valstruct cur_value;
+ vtw_sorted cur_sorted;
+ vtw_sorted act_sorted;
+
+ ok = TRUE;
+ actions = NULL;
+ if(mode == del_mode || mode == do_del_mode) {
+ creating = FALSE;
+ if (defp && defp->actions[delete_act].vtw_list_head) {
+ set_in_delete_action(TRUE);
+ actions = defp->actions[delete_act].vtw_list_head;
+ }
+ } else {
+ creating = TRUE;
+ if (defp && defp->actions[create_act].vtw_list_head)
+ actions = defp->actions[create_act].vtw_list_head;
+ }
+ /* prepare cur_value */
+
+ status = char2val(defp, cp, &cur_value);
+ if (mode != do_del_mode && mode != create_mode) {
+ /* get active value */
+ switch_path(APATH); /* switch form CCD to ACD */
+ status = get_value(&cp, &m_path);
+ switch_path(CPATH); /* back to CCD */
+ if (status != VTWERR_OK) {
+ no_shadow = TRUE;
+ }else
+ no_shadow = FALSE;
+ } else {
+ no_shadow = TRUE;
+ }
+ vtw_sort(&cur_value, &cur_sorted);
+ if(no_shadow) {
+ act_sorted.num = 0;
+ }else {
+ status = char2val(defp, cp, &act_value);
+ if (status != VTWERR_OK) {
+ INTERNAL;
+ }
+ /* sort them */
+ vtw_sort(&act_value, &act_sorted);
+ }
+ if (mode == do_del_mode) {
+ /* it was actually act_sorted, not cur_sorted */
+ act_sorted = cur_sorted;
+ cur_sorted.num = 0;
+ act_value = cur_value;
+ /* act_value will be freed by freeing cur_value
+ do not zero out it here */
+ }
+
+ acti = 0;
+ curi = 0;
+ total = act_sorted.num + cur_sorted.num;
+ a_res=0;
+ c_res=0;
+ a_ptr = my_malloc(total*sizeof(char *), "");
+ c_ptr = my_malloc(total*sizeof(char *), "");
+ while (acti < act_sorted.num || curi < cur_sorted.num) {
+ if (acti == act_sorted.num) {
+ cur_pr_val = TRUE;
+ pr_index = curi;
+ sign = +1;
+ ++curi;
+ } else if (curi == cur_sorted.num) {
+ cur_pr_val = FALSE;
+ pr_index = acti;
+ sign = -1;
+ ++acti;
+ } else {
+ /* compare */
+ actp = act_sorted.ptrs[acti];
+ curp = cur_sorted.ptrs[curi];
+ /* compare */
+ if (act_sorted.partnum){
+ for(partnum = 0; partnum < act_sorted.partnum;
+ ++partnum) {
+ res = *((int *)actp + partnum) -
+ *((int *)curp + partnum);
+ if (res)
+ break;
+ }
+ } else{
+ res = strcmp((char *)actp, (char *) curp);
+ }
+ if (res == 0) {
+ /* the same */
+ cur_pr_val = TRUE;
+ pr_index = curi;
+ sign = 0;
+ ++acti;
+ ++curi;
+ } else if (res < 0) {
+ /* act < cur, act is unmatched */
+ cur_pr_val = FALSE;
+ pr_index = acti;
+ sign = -1;
+ ++acti;
+ }else {
+ /* cur < act, cur is unmatched */
+ cur_pr_val = TRUE;
+ pr_index = curi;
+ sign = 1;
+ ++curi;
+ }
+ }
+ if (defp->def_type == TEXT_TYPE ||
+ defp->def_type == BOOL_TYPE) {
+ val_string = cur_pr_val?
+ ((char *)(cur_sorted.ptrs[pr_index])):
+ ((char *)(act_sorted.ptrs[pr_index]));
+ } else {
+ if (cur_pr_val) {
+ int elem = (((unsigned int *)(cur_sorted.ptrs[pr_index]))-
+ cur_sorted.parts)/
+ cur_sorted.partnum;
+ val_string = cur_value.cnt?cur_value.vals[elem]:
+ cur_value.val;
+ } else {
+ int elem = (((unsigned int *)(act_sorted.ptrs[pr_index]))-
+ act_sorted.parts)/
+ act_sorted.partnum;
+ val_string = act_value.cnt?act_value.vals[elem]:
+ act_value.val;
+ }
+ }
+ set_at_string(val_string);
+ switch (sign) {
+ case 0: /* found in both, no actions, include in both */
+ a_ptr[a_res++]=val_string;
+ c_ptr[c_res++]=val_string;
+ break;
+ case 1: /* found only in change */
+ if (ok && creating) {
+ if (actions) {
+ /* do create action */
+ ok = execute_list(actions, defp);
+ } else if (defp && defp->actions[update_act].vtw_list_head) {
+ /* no create action => use update action */
+ ok = execute_list(defp->actions[update_act].vtw_list_head, defp);
+ }
+ if (ok && defp && defp->actions[activate_act].vtw_list_head) {
+ /* try activate action */
+ ok = execute_list(defp->actions[activate_act].vtw_list_head, defp);
+ }
+ /* if succ, make it look old */
+ if(ok)
+ a_ptr[a_res++]=val_string;
+ }
+ c_ptr[c_res++]=val_string; /* in all cases */
+ break;
+ case -1: /* found only in working */
+ if (ok && !creating && actions) {/* ok and deleting */
+ ok = execute_list(actions, defp);
+ }
+ /* if succ and deleting - do nothing, else */
+ if (!ok || creating)
+ a_ptr[a_res++]=val_string;
+ }
+ }
+ if (creating && ok)
+ c_res = 0;
+#if BITWISE
+ if (!in_txn) {/* ELSE WAIT TILL THE END OF TXN */
+ switch_path(APATH);
+ if (a_res) {
+ make_dir();
+ push_path(&m_path, VAL_NAME);
+ fp = fopen(m_path.path, "w");
+ if (fp == NULL)
+ bye("Can not open value file %s", m_path.path);
+ for(i=0;i<a_res;++i)
+ if (fputs(a_ptr[i], fp) < 0 || fputc('\n',fp) < 0)
+ bye("Error writing file %s", m_path.path);
+ fclose(fp);
+ pop_path(&m_path);
+ }else{
+ perform_delete_node();
+ }
+ switch_path(CPATH);
+ if (c_res) {
+ make_dir();
+ push_path(&m_path, VAL_NAME);
+ fp = fopen(m_path.path, "w");
+ if (fp == NULL)
+ bye("Can not open value file %s", m_path.path);
+ for(i=0;i<c_res;++i)
+ if (fputs(c_ptr[i], fp) < 0 || fputc('\n',fp) < 0)
+ bye("Error writing file %s", m_path.path);
+ fclose(fp);
+ pop_path(&m_path);
+ }else{
+ perform_delete_node();
+ }
+ }
+#endif /*BITWISE*/
+ if (mode == do_del_mode)
+ switch_path(APATH);
+ else
+ switch_path(CPATH);
+ if(act_sorted.num)
+ free_sorted(&act_sorted);
+ if(cur_sorted.num)
+ free_sorted(&cur_sorted);
+ my_free(a_ptr);
+ my_free(c_ptr);
+ free_val(&cur_value);
+ if (!no_shadow)
+ free_val(&act_value);
+ set_in_delete_action(FALSE);
+ return ok;
+}
+
+static int fin_commit(boolean ok)
+{
+ char *command;
+ static char format1[]="cp -r -f %s/* %s"; /*mdirp, tmpp*/
+ static char format2[]="sudo umount %s"; /*mdirp*/
+ static char format3[]="rm -f %s/" MOD_NAME " >&/dev/null ; /bin/true";
+ /*tmpp*/
+ static char format4[]="rm -rf %s/{.*,*} >&/dev/null ; /bin/true"; /*cdirp*/
+ static char format5[]="rm -rf %s/{.*,*} >&/dev/null ; /bin/true"; /*adirp*/
+ static char format6[]="mv -f %s/* -t %s";/*tmpp, adirp*/
+ static char format7[]="sudo mount -t unionfs -o dirs=%s=rw:%s=ro"
+ " unionfs %s"; /*cdirp, adirp, mdirp*/
+ int m_len = strlen(get_mdirp());
+ int t_len = strlen(get_tmpp());
+ int c_len = strlen(get_cdirp());
+ int a_len = strlen(get_adirp());
+ set_echo(TRUE);
+ if (!ok){
+ printf("Commit FAILED!\n");
+ return -1;
+ }
+ command = my_malloc(strlen(format1) + m_len + t_len, "");
+ sprintf(command, format1, get_mdirp(), get_tmpp());
+ system(command);
+ my_free(command);
+
+ command = my_malloc(strlen(format2) + m_len, "");
+ sprintf(command, format2, get_mdirp());
+ system(command);
+ my_free(command);
+
+ command = my_malloc(strlen(format3) + c_len, "");
+ sprintf(command, format3, get_tmpp());
+ system(command);
+ my_free(command);
+
+ command = my_malloc(strlen(format4) + c_len, "");
+ sprintf(command, format4, get_cdirp());
+ system(command);
+ my_free(command);
+
+ command = my_malloc(strlen(format5) + a_len, "");
+ sprintf(command, format5, get_adirp());
+ system(command);
+ my_free(command);
+
+ command = my_malloc(strlen(format6) + t_len + a_len, "");
+ sprintf(command, format6, get_tmpp(), get_adirp());
+ system(command);
+ my_free(command);
+
+ command = my_malloc(strlen(format7) + c_len + a_len + m_len, "");
+ sprintf(command, format7, get_cdirp(), get_adirp(), get_mdirp());
+ system(command);
+ my_free(command);
+
+ return 0;
+}
+