#include #include #include #include #include #include #include #include #include #include "common/defs.h" #include "common/unionfs.h" boolean g_debug; extern vtw_path m_path; extern vtw_path t_path; /*** functions for filesystem operations ***/ /* these functions replace the system() invocations that were a major source * of commit overhead. note that they currently duplicate the previous * behavior so no status is returned (since system() return code was not * checked). this should probably be changed so each invocation is checked * for error. */ static inline void sys_mkdir_p(const char *path) { if (g_mkdir_with_parents(path, 0775) != 0) { /* error */ return; } } static inline void sys_rm(const char *file) { GFile *target = g_file_new_for_path(file); if (!g_file_delete(target, NULL, NULL)) { /* error */ return; } } static inline void sys_cp(const char *src_file, const char *dst_file) { GFile *src = g_file_new_for_path(src_file); GFile *dst = g_file_new_for_path(dst_file); if (!g_file_copy(src, dst, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, NULL)) { /* error */ return; } } void retrieve_data(char* rel_data_path, GNode *node, char* root, NODE_OPERATION op); void apply_priority(GNode *root_node); void set_path(char *path, boolean config); struct VyattaNode* copy_vyatta_node(struct VyattaNode* vn); void get_term_data_values(GNode *node); void dlist_test_func(GQuark key_id,gpointer data,gpointer user_data); void match_priority_node(GNode *node, gchar** tok_str, int pri); GNode* insert_sibling_in_order(GNode *parent, GNode *child); static gboolean copy_func(GNode *node, gpointer data); static gboolean delete_func(GNode *node, gpointer data); static gboolean delete_wh_func(GNode *node, gpointer data); static void piecewise_copy(GNode *root_node, boolean test_mode); /** * * Data is stored on the path: /opt/vyatta/config/tmp/new_config_5425/system/login/user/foo/authentication/plaintext-password Config is stored along this path: /opt/vyatta/config/template/vyatta-cfg/system/login/user/node.tag/authentication/plaintext-password 1) Need to split out relative path 2) if node is *MULTI* then the path value is the actual value 3) For the config copy the pointer from an existing config value for each multinode This should allow a combined data/config tree * * * **/ char* get_config_path(GNode *node) { char *buf; buf = malloc(MAX_LENGTH_DIR_PATH*sizeof(char)); buf[0] = '/'; buf[1] = '\0'; if (node == NULL) { return NULL; } GNode *n = node; while (G_NODE_IS_ROOT(n) != TRUE) { struct VyattaNode *d = (struct VyattaNode*)n->data; if (d == NULL) { if (g_debug) { printf("unionfs::get_config_path(): data ptr is null\n"); syslog(LOG_DEBUG,"unionfs::get_config_path(): data ptr is null"); } return NULL; } //if not found, mark current node as multinode if (d->_data._name != NULL) { char tmp[MAX_LENGTH_DIR_PATH]; strcpy(tmp,buf); //need to check the configuration location for the existance of this node to determine if it's a multi if (G_NODE_IS_ROOT(n->parent) != TRUE && ((struct VyattaNode*)(n->parent->data))->_config._multi == TRUE) { sprintf(buf,"node.tag/%s",tmp); } else { sprintf(buf,"%s/%s",d->_data._name,tmp); } } n = n->parent; } return buf; } /** * **/ void retrieve_data(char* rel_data_path, GNode *node, char* root, NODE_OPERATION op) { boolean final_node = FALSE; if (node == NULL) { return; } char *tmp = root;//get_cdirp(); char full_data_path[sizeof(char)*MAX_LENGTH_DIR_PATH]; strcpy(full_data_path,tmp); if (rel_data_path != NULL) { strcat(full_data_path,rel_data_path); } if (g_debug) { printf("unionfs::retrieve_data(): %s\n", full_data_path); syslog(LOG_DEBUG,"unionfs::retrieve_data(): %s\n", full_data_path); } //WE'LL WANT TO DO SOMETHING LIKE--IS THIS NODE A VALUE NODE, THEN PROCESS. //now check for value at this node and copy char *cp = NULL; //data_path needs to be of type vtw_path!! vtw_path vpath; init_path(&vpath, full_data_path); //lstat for value file BEFORE retrieving if (value_exists(full_data_path) && get_value(&cp, &vpath) == 0) { //terminating value need to create new terminating node here! struct VyattaNode *vn = calloc(1,sizeof(struct VyattaNode)); GNode *new_node = g_node_new(vn); new_node = insert_sibling_in_order(node,new_node); // new_node = g_node_insert(node, -1, new_node); vn->_data._name = cp; vn->_data._value = FALSE;//TRUE; //data value vn->_data._operation = op; vn->_priority = LOWEST_PRIORITY; vn->_data._path = malloc(MAX_LENGTH_DIR_PATH*sizeof(char)); sprintf(vn->_data._path,"%s",rel_data_path); final_node = TRUE; } /* NOTE: will need to implement ptr copy over for the config data that traverses up multi-nodes. Currently this will just read the data from the parser AGAIN, but in the name of memory and performance efficiency this should be a copy of the already processed data. */ //Now dig up the configuration data char *config_path = get_config_path(node); if (config_path != NULL) { char *conf_base = (char*)get_tdirp(); char buf[MAX_LENGTH_HELP_STR]; sprintf(buf,"%s/%snode.def",conf_base,config_path); struct stat s; if (g_debug) { printf("unionfs::retrieve_data(): config path: %s\n",buf); syslog(LOG_DEBUG,"unionfs::retrieve_data(): config path: %s\n",buf); } struct VyattaNode* vn = (struct VyattaNode*)node->data; vtw_def def; memset(&def, 0, sizeof(def)); if ((lstat(buf,&s) == 0) && S_ISREG(s.st_mode)) { if (parse_def(&def, buf, FALSE) == 0) { if (g_debug) { printf("[FOUND node.def]"); syslog(LOG_DEBUG,"[FOUND node.def]"); } //either multi or tag--shouldn't have made a difference, but arkady was confused. vn->_config._multi = (def.tag | def.multi); vn->_priority = def.def_priority; } } vn->_config._def = def; vn->_config._path = config_path; char *tmp = malloc(sizeof(char)*MAX_LENGTH_DIR_PATH); strcpy(tmp,rel_data_path); strcat(tmp,"/"); vn->_data._path = tmp; //will stamp an embedded node as a value node if (G_NODE_IS_ROOT(node) == FALSE) { struct VyattaNode* vn_parent = (struct VyattaNode*)node->parent->data; if (vn_parent->_config._multi == TRUE) { ((struct VyattaNode*)node->data)->_data._value = TRUE; //now let's patch up the def multi-nodes //move the def for the multinode from the parent to the value node struct VyattaNode* vn2 = (struct VyattaNode*)node->data; if (final_node == FALSE) { //non-term multi if (g_node_n_children(node->parent) == 1) { vn2->_config._def = ((struct VyattaNode*)node->parent->data)->_config._def; memset(&((struct VyattaNode*)node->parent->data)->_config._def, 0, sizeof(vtw_def)); } else { //find node other than myself to copy defs across GNode *first_child = g_node_first_child(node->parent); if (first_child == node) { first_child = g_node_next_sibling(first_child); } vn2->_config._def = ((struct VyattaNode*)first_child->data)->_config._def; } } } } if (g_debug) { printf("\n"); } } if (final_node == TRUE) { //move defs to child... get_term_data_values(node); //fix operation on parent because this is a final node char buf[MAX_LENGTH_HELP_STR]; sprintf(buf,"%s/%s",get_adirp(),rel_data_path); struct stat s; if (lstat(buf,&s) != 0) { struct VyattaNode* vn = (struct VyattaNode*)node->data; vn->_data._operation = K_CREATE_OP; } return; } //iterate over directory here DIR *dp; if ((dp = opendir(full_data_path)) == NULL){ if (g_debug) { //could also be a terminating value now printf("unionfs::retrieve_data(), failed to open directory: %s\n", full_data_path); syslog(LOG_DEBUG,"unionfs::retrieve_data(), failed to open directory: %s\n", full_data_path); } return; } //finally iterate over valid child directory entries boolean processed = FALSE; boolean whiteout_file_found = FALSE; struct dirent *dirp = NULL; while ((dirp = readdir(dp)) != NULL) { if (strcmp(dirp->d_name,WHITEOUT_FILE) == 0) { whiteout_file_found = TRUE; } if (strcmp(dirp->d_name, ".") != 0 && strcmp(dirp->d_name, "..") != 0 && strcmp(dirp->d_name, MODIFIED_FILE) != 0 && strcmp(dirp->d_name, DEF_FILE) != 0 && strcmp(dirp->d_name, WHITEOUT_FILE) != 0 && strcmp(dirp->d_name, VALUE_FILE) != 0) { processed = TRUE; char *data_buf = malloc(MAX_LENGTH_DIR_PATH*sizeof(char)); if (strncmp(dirp->d_name,DELETED_NODE,4) == 0) { strcpy(data_buf,dirp->d_name+4); //SKIP THE .WH. //create new node and insert... struct VyattaNode *vn = calloc(1,sizeof(struct VyattaNode)); vn->_data._name = data_buf; vn->_data._value = FALSE; vn->_data._operation = K_DEL_OP; vn->_priority = LOWEST_PRIORITY; char new_data_path[MAX_LENGTH_DIR_PATH]; sprintf(new_data_path,"%s/%s",rel_data_path,data_buf); GNode *new_node = g_node_new(vn); // new_node = g_node_insert(node, -1, new_node); new_node = insert_sibling_in_order(node,new_node); //will need to enter a special recursion against the active configuration to mark nested delete nodes retrieve_data(new_data_path,new_node,get_adirp(),K_DEL_OP); } else { strcpy(data_buf,dirp->d_name); //create new node and insert... struct VyattaNode *vn = calloc(1,sizeof(struct VyattaNode)); vn->_data._name = data_buf; vn->_data._value = FALSE; vn->_priority = LOWEST_PRIORITY; char new_data_path[MAX_LENGTH_DIR_PATH]; sprintf(new_data_path,"%s/%s",rel_data_path,data_buf); char active_data_path[MAX_LENGTH_DIR_PATH]; sprintf(active_data_path,"%s%s",get_adirp(),rel_data_path); struct stat s; if (lstat(active_data_path,&s) == 0) { vn->_data._operation = K_NO_OP; } else { vn->_data._operation = K_CREATE_OP; ((struct VyattaNode*)node->data)->_data._operation = K_CREATE_OP; } //set recursed entry op to del if (op == K_DEL_OP) { vn->_data._operation = K_DEL_OP; } GNode *new_node = g_node_new(vn); // new_node = g_node_insert(node, -1, new_node); new_node = insert_sibling_in_order(node,new_node); if (op == K_DEL_OP) { retrieve_data(new_data_path,new_node,get_adirp(),vn->_data._operation); } else { retrieve_data(new_data_path,new_node,root,vn->_data._operation); } } } } //catch hanging case where embedded multinode is new with no children if (processed == FALSE) { char active_data_path[MAX_LENGTH_DIR_PATH]; sprintf(active_data_path,"%s/%s",get_adirp(),rel_data_path); struct stat s; if ((lstat(active_data_path,&s) != 0)) { ((struct VyattaNode*)node->data)->_data._operation = K_CREATE_OP; } } closedir(dp); //if there is a ".wh.__dir_opaque" and were not already //iterating the active dir then test for a hidden deletion if (whiteout_file_found == TRUE && op != K_DEL_OP) { //scan active dir for entry not found in tmp DIR *dp_wo; //build active directory for this... char active_data_path[MAX_LENGTH_DIR_PATH]; sprintf(active_data_path,"%s%s",get_adirp(),rel_data_path); if ((dp_wo = opendir(active_data_path)) != NULL) { if (g_debug) { //could also be a terminating value now syslog(LOG_DEBUG,"unionfs::retrieve_data(), failed to open directory: %s\n", active_data_path); printf("unionfs::retrieve_data(), failed to open directory: %s\n", active_data_path); } struct dirent *dirp_wo = NULL; while ((dirp_wo = readdir(dp_wo)) != NULL) { char tmp_new_data_path[MAX_LENGTH_DIR_PATH]; sprintf(tmp_new_data_path,"%s/%s/%s",get_cdirp(),rel_data_path,dirp_wo->d_name); struct stat s; if (lstat(tmp_new_data_path,&s) != 0) { //create new node and insert... struct VyattaNode *vn = calloc(1,sizeof(struct VyattaNode)); char *data_buf = malloc(MAX_LENGTH_DIR_PATH*sizeof(char)); strcpy(data_buf,dirp_wo->d_name); vn->_data._name = data_buf; vn->_data._value = FALSE; vn->_data._operation = K_DEL_OP; vn->_priority = LOWEST_PRIORITY; GNode *new_node = g_node_new(vn); new_node = insert_sibling_in_order(node,new_node); char new_data_path[MAX_LENGTH_DIR_PATH]; sprintf(new_data_path,"%s/%s",rel_data_path,dirp_wo->d_name); retrieve_data(new_data_path,new_node,get_adirp(),K_DEL_OP); } } closedir(dp_wo); } } return; } /** * **/ GNode* common_get_local_session_data() { //get root directory char *data_path = malloc(sizeof(char)*MAX_LENGTH_DIR_PATH); data_path[0] = '\0'; struct VyattaNode *vn = calloc(1,sizeof(struct VyattaNode)); vn->_data._name = NULL; //root node has null vn->_data._operation = K_NO_OP; vn->_priority = LOWEST_PRIORITY; //create first node GNode *root_node = g_node_new(vn); //iterate through recursive calls to parse_new() calls (see original commit()) retrieve_data(data_path,root_node,get_cdirp(),K_SET_OP); apply_priority(root_node); return root_node; } /** * **/ boolean value_exists(char *path) { char buf[MAX_LENGTH_DIR_PATH]; sprintf(buf, "%s/%s",path,VALUE_FILE); struct stat stat_buf; return !stat(buf,&stat_buf); } /** * need to compute parent as it might not be in the node structure * **/ void common_set_parent_context(char *cpath, char *dpath) { if (g_debug) { printf("common_set_parent_context(incoming): %s, %s\n",cpath,dpath); syslog(LOG_DEBUG,"common_set_parent_context(incoming): %s, %s\n",cpath,dpath); } //strip off last path and set int index = strlen(cpath)-1; if (cpath[index] == '/') { while (TRUE) { if (cpath[--index] != '/') { cpath[index] = '\0'; break; } } } char *ptr = rindex(cpath,'/'); if (ptr != NULL) { *ptr = '\0'; } set_path(cpath,TRUE); index = strlen(dpath)-1; if (dpath[index] == '/') { while (TRUE) { if (dpath[--index] != '/') { dpath[index] = '\0'; break; } } } ptr = rindex(dpath,'/'); if (ptr != NULL) { *ptr = '\0'; } set_path(dpath,FALSE); if (g_debug) { printf("common_set_parent_context: %s, %s\n",cpath,dpath); syslog(LOG_DEBUG,"common_set_parent_context: %s, %s\n",cpath,dpath); } } /** * **/ void common_set_context(char *cpath, char *dpath) { if (g_debug) { printf("common_set_context: %s, %s\n",cpath,dpath); syslog(LOG_DEBUG,"common_set_context: %s, %s\n",cpath,dpath); } set_path(cpath,TRUE); set_path(dpath,FALSE); } /** * **/ void set_path(char *path, boolean config) { //set t_path, m_path //tokenize path and iterate if (config == FALSE) { init_path(&m_path, get_mdirp()); } else { init_path(&t_path, get_tdirp()); } char* start_ptr = NULL; char* end_ptr = NULL; if (path == NULL) { if (g_debug) { printf("unionfs::set_path() null value on entry\n"); syslog(LOG_DEBUG,"unionfs::set_path() null value on entry\n"); } return; } start_ptr = path; while((end_ptr = index(start_ptr+1,'/')) != NULL) { char tmp[1024]; if (*start_ptr == '/') { ++start_ptr; } int size = end_ptr-start_ptr; if (size < 1 || size > 1024) { /* if (g_debug) { if (config == FALSE) { printf("unionfs::set_path(): %s, %s\n", path,m_path.path); } else { printf("unionfs::set_path(): %s, %s\n", path,t_path.path); } } */ return; } memcpy(tmp, start_ptr, size); tmp[size] = '\0'; if (config == FALSE) { push_path_no_escape(&m_path, tmp); //data } else { push_path_no_escape(&t_path, tmp); //config } start_ptr = end_ptr; } /* if (g_debug) { if (config == FALSE) { printf("unionfs::set_path(): %s, %s\n", path,m_path.path); } else { printf("unionfs::set_path(): %s, %s\n", path,t_path.path); } } */ } /** * NEED TO PROTECT AGAINST NESTED COMMIT NODES. CANNOT BE SUPPORTED * IN CURRENT HIERARCHICAL STRUCTURE WITHOUT CHANGING HOW UNDERLYING * SYSTEM MAINTAINS DATA. * **/ void common_commit_copy_to_live_config(GNode *node, boolean suppress_piecewise_copy, boolean test_mode) { //first check for existence of path before committing char *path = ((struct VyattaNode*)(node->data))->_data._path; if (g_debug) { printf("common_commit_copy_to_live_config(): %s\n",path); syslog(LOG_DEBUG,"common_commit_copy_to_live_config(): %s\n",path); } char *command = malloc(MAX_LENGTH_DIR_PATH); static const char format0[]="mkdir -p %s ; /bin/true"; static const char formatpoint5[]="rm -fr %s"; /*tmpp*/ static const char format1[]="cp -r -f %s/* %s"; /*mdirp, tmpp*/ static const char format1point1[]="mv -f %s/* -t %s"; /*mdirp, tmpp*/ static const char format1point5[]="rm -fr %s/*"; /*tmpp*/ static const char format2[]="sudo umount %s"; //mdirp static const char format8[]="sudo mount -t unionfs -o dirs=%s=rw:%s=ro unionfs %s"; //cdirp, adirp, mdirp set_echo(TRUE); char mbuf[MAX_LENGTH_DIR_PATH]; sprintf(mbuf,"%s%s",get_mdirp(),path); char cbuf[MAX_LENGTH_DIR_PATH]; sprintf(cbuf,"%s%s",get_cdirp(),path); char tbuf[MAX_LENGTH_DIR_PATH]; sprintf(tbuf,"%s%s",get_tmpp(),path); char abuf[MAX_LENGTH_DIR_PATH]; sprintf(abuf,"%s%s",get_adirp(),path); char mbuf_root[MAX_LENGTH_DIR_PATH]; sprintf(mbuf_root,"%s",get_mdirp()); char cbuf_root[MAX_LENGTH_DIR_PATH]; sprintf(cbuf_root,"%s",get_cdirp()); char tbuf_root[MAX_LENGTH_DIR_PATH]; sprintf(tbuf_root,"%s",get_tmpp()); char abuf_root[MAX_LENGTH_DIR_PATH]; sprintf(abuf_root,"%s",get_adirp()); //only operate on path if it exists //have to clean out tbuf before copying sprintf(command, formatpoint5, tbuf); if (g_debug) { printf("%s\n",command); syslog(LOG_DEBUG,"%s\n",command); fflush(NULL); } if (test_mode == FALSE) { system(command); } //mkdir temp merge sprintf(command,format0,tbuf); if (g_debug) { printf("%s\n",command); syslog(LOG_DEBUG,"%s\n",command); fflush(NULL); } if (test_mode == FALSE) { system(command); } //cp merge to temp merge sprintf(command, format1, mbuf, tbuf); if (g_debug) { printf("%s\n",command); syslog(LOG_DEBUG,"%s\n",command); fflush(NULL); } if (test_mode == FALSE) { system(command); } //unmount temp (i.e. rm merge) sprintf(command, format2, mbuf_root); if (g_debug) { printf("%s\n",command); syslog(LOG_DEBUG,"%s\n",command); fflush(NULL); } if (test_mode == FALSE) { system(command); } if (suppress_piecewise_copy) { sprintf(command, format1point5, abuf_root); if (g_debug) { printf("%s\n",command); syslog(LOG_DEBUG,"%s\n",command); fflush(NULL); } if (test_mode == FALSE) { system(command); } sprintf(command, format1point1, tbuf_root, abuf_root); if (g_debug) { printf("%s\n",command); syslog(LOG_DEBUG,"%s\n",command); fflush(NULL); } if (test_mode == FALSE) { system(command); } } else { piecewise_copy(node, test_mode); } sprintf(command, format8, cbuf_root,abuf_root,mbuf_root); if (g_debug) { printf("%s\n",command); syslog(LOG_DEBUG,"%s\n",command); fflush(NULL); } if (test_mode == FALSE) { system(command); } fflush(NULL); free(command); return; } //needed for iteration below struct SrcDst { char *_src; char *_dst; boolean _test_mode; }; /** * **/ void common_commit_clean_temp_config(GNode *root_node, boolean test_mode) { if (g_debug) { printf("common_commit_clean_temp_config()\n"); syslog(LOG_DEBUG,"common_commit_clean_temp_config()\n"); } //first clean up the root // common_commit_copy_to_live_config("/"); char *command; command = malloc(MAX_LENGTH_DIR_PATH); static const char format2[]="sudo umount %s"; //mdirp static const char format3[]="rm -f %s/" MOD_NAME " >&/dev/null ; /bin/true"; /*tmpp*/ static const char format5[]="rm -fr %s/{.*,*} >&/dev/null ; /bin/true"; /*cdirp*/ static const char format7[]="sudo mount -t unionfs -o dirs=%s=rw:%s=ro unionfs %s"; //cdirp, adirp, mdirp char tbuf[MAX_LENGTH_DIR_PATH]; sprintf(tbuf,"%s",get_tmpp()); char cbuf[MAX_LENGTH_DIR_PATH]; sprintf(cbuf,"%s",get_cdirp()); char mbuf[MAX_LENGTH_DIR_PATH]; sprintf(mbuf,"%s",get_mdirp()); char abuf[MAX_LENGTH_DIR_PATH]; sprintf(abuf,"%s",get_adirp()); set_echo(TRUE); sprintf(command, format2, mbuf); if (g_debug) { printf("%s\n",command); syslog(LOG_DEBUG,"%s\n",command); fflush(NULL); } if (test_mode == FALSE) { system(command); } /* * Need to add to the following func below to clean up dangling .wh. files. * This pass needs to be prior to the commands below (but after the umount). * This fixes a bug when higher priority root nodes are deleted and not removed. */ //Iterate through node hierarchy and remove deleted nodes from active config--insurance //to protect against priority whiteouts in parent/child order //TOP DOWN if (root_node != NULL) { struct SrcDst sd; sd._test_mode = test_mode; g_node_traverse(root_node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, (GNodeTraverseFunc)delete_wh_func, (gpointer)&sd); } sprintf(command, format3, tbuf); if (g_debug) { printf("%s\n",command); syslog(LOG_DEBUG,"%s\n",command); fflush(NULL); } if (test_mode == FALSE) { system(command); } sprintf(command, format3, cbuf); if (g_debug) { printf("%s\n",command); syslog(LOG_DEBUG,"%s\n",command); fflush(NULL); } if (test_mode == FALSE) { system(command); } sprintf(command, format5, cbuf); if (g_debug) { printf("%s\n",command); syslog(LOG_DEBUG,"%s\n",command); fflush(NULL); } if (test_mode == FALSE) { system(command); } sprintf(command, format7, cbuf,abuf,mbuf); if (g_debug) { printf("%s\n",command); syslog(LOG_DEBUG,"%s\n",command); fflush(NULL); } if (test_mode == FALSE) { system(command); } /* notify other users in config mode */ system("/opt/vyatta/sbin/vyatta-cfg-notify"); free(command); return; } void match_priority_node(GNode *node, gchar** tok_str, int pri) { int index = g_node_depth(node)-1; if (tok_str[index] == NULL) { //MATCHED TO THE END ((struct VyattaNode*)node->data)->_priority = pri; return; } //iterate over children for a potential match GNode *child = g_node_first_child(node); while (child != NULL) { char *compval = ((struct VyattaNode*)child->data)->_data._name; if (strncmp(compval,tok_str[index],strlen(compval)) == 0) { match_priority_node(child,tok_str,pri); } else if (strncmp(NODE_TAG_FILE,tok_str[index],strlen(NODE_TAG_FILE)) == 0) { match_priority_node(child,tok_str,pri); } else if (strncmp("*",tok_str[index],1) == 0) { //wildcard match_priority_node(child,tok_str,pri); } child = g_node_next_sibling(child); } return; } /** * * **/ void set_node_priority(GNode *root_node,char* path,unsigned long pri) { if (root_node == NULL) { return; } gchar** tok_str = g_strsplit(path,"/",MAX_DEPTH); struct PriData pri_data; pri_data._pri = pri; pri_data._tok_str = tok_str; match_priority_node(root_node,tok_str,pri); g_strfreev(tok_str); return; } /** * **/ void apply_priority(GNode *root_node) { //parse file and find node and set value FILE *fp = fopen(NODE_PRIORITY_FILE, "r"); if (fp != NULL) { if (g_debug) { printf("unionfs::apply_priority(), found priority file\n"); syslog(LOG_DEBUG,"unionfs::apply_priority(), found priority file\n"); } char str[1025]; while (fgets(str, 1024, fp) != 0) { gchar** tok_str = g_strsplit(str," ",3); if (tok_str[0] == NULL || tok_str[1] == NULL || strncmp(tok_str[0],"#",1) == 0) { continue; } char *path = tok_str[1]; if (g_debug) { printf("unionfs::apply_priority(), working on this %s\n",path); syslog(LOG_DEBUG,"unionfs::apply_priority(), working on this %s\n",path); } //now apply to node set_node_priority(root_node,path,strtoul(tok_str[0],NULL,10)); g_strfreev(tok_str); } fclose(fp); } } /** * **/ struct VyattaNode* copy_vyatta_node(struct VyattaNode* vn) { struct VyattaNode *new_vn = calloc(1,sizeof(struct VyattaNode)); if (vn->_data._name != NULL) { new_vn->_data._name = malloc(MAX_LENGTH_DIR_PATH*sizeof(char)); strcpy(new_vn->_data._name,vn->_data._name); } new_vn->_data._value = vn->_data._value; if (vn->_data._path != NULL) { new_vn->_data._path = malloc(MAX_LENGTH_DIR_PATH*sizeof(char)); strcpy(new_vn->_data._path,vn->_data._path); } new_vn->_data._operation = vn->_data._operation; new_vn->_priority = vn->_priority; new_vn->_config._multi = new_vn->_config._multi; // new_vn->_config._def = new_vn->_config._def; //cpy this? if (vn->_config._default != NULL) { new_vn->_config._default = malloc(MAX_LENGTH_DIR_PATH*sizeof(char)); strcpy(new_vn->_config._default,vn->_config._default); } if (vn->_config._path != NULL) { new_vn->_config._path = malloc(MAX_LENGTH_DIR_PATH*sizeof(char)); strcpy(new_vn->_config._path,vn->_config._path); } return new_vn; } /** * **/ void get_term_data_values(GNode *node) { struct VyattaNode* vn = (struct VyattaNode*)node->data; char full_new_data_path[MAX_LENGTH_DIR_PATH]; char full_active_data_path[MAX_LENGTH_DIR_PATH]; sprintf(full_active_data_path,"%s/%s",get_adirp(),vn->_data._path); sprintf(full_new_data_path,"%s/%s",get_mdirp(),vn->_data._path); GData *datalist; g_datalist_init(&datalist); //now check for value at this node and copy char *cp = NULL; //data_path needs to be of type vtw_path!! vtw_path vpath; //lstat for value file BEFORE retrievin gchar **tok_str_new = NULL; gchar **tok_str_active = NULL; //create full_data_path here init_path(&vpath, full_active_data_path); if (value_exists(full_active_data_path) && get_value(&cp, &vpath) == 0) { tok_str_active = g_strsplit(cp,"\n",0); } init_path(&vpath, full_new_data_path); if (value_exists(full_new_data_path) && get_value(&cp, &vpath) == 0) { tok_str_new = g_strsplit(cp,"\n",0); } if (vn->_config._multi == TRUE) { //add active elements int i; for (i = 0; tok_str_active != NULL && tok_str_active[i] != NULL; ++i) { struct ValueData *data; data = (struct ValueData*)calloc(1, sizeof(struct ValueData)); //HANDLE EMPTY NEW CONFIG data->_state = K_DEL_OP; g_datalist_set_data(&datalist, tok_str_active[i], data); } //add value elements not found in set yet for (i = 0; tok_str_new != NULL && tok_str_new[i] != NULL; ++i) { gpointer g; if ((g = g_datalist_get_data(&datalist,tok_str_new[i])) == NULL) { struct ValueData *data; data = (struct ValueData*)calloc(1, sizeof(struct ValueData)); data->_state = K_CREATE_OP; g_datalist_set_data(&datalist, tok_str_new[i], data); } else { ((struct ValueData*)g)->_state = K_NO_OP; } } } else { //leaf struct ValueData *data; data = (struct ValueData*)calloc(1, sizeof(struct ValueData)); if ((tok_str_active == NULL || tok_str_active[0] == NULL) && (tok_str_new == NULL || tok_str_new[0] == NULL)) { cp = malloc(MAX_LENGTH_DIR_PATH*sizeof(char)); cp[0] = '\0'; data->_state = ((struct VyattaNode*)node->parent->data)->_data._operation; g_datalist_set_data(&datalist, cp, data); } else if (tok_str_active == NULL || tok_str_active[0] == NULL) { data->_state = K_CREATE_OP; g_datalist_set_data(&datalist, tok_str_new[0], data); } else if (tok_str_new == NULL || tok_str_new[0] == NULL) { data->_state = K_DEL_OP; g_datalist_set_data(&datalist, tok_str_active[0], data); } else { if (strcmp(tok_str_active[0],tok_str_new[0]) != 0) { data->_state = K_SET_OP; g_datalist_set_data(&datalist, tok_str_new[0], data); } else { data->_state = K_NO_OP; g_datalist_set_data(&datalist, tok_str_new[0], data); } } } //now let's process the node's values.... g_datalist_foreach(&datalist, dlist_test_func, node); struct VyattaNode *vn_parent = (struct VyattaNode*)node->data; memset(&vn_parent->_config._def, 0, sizeof(vtw_def)); g_strfreev(tok_str_new); g_strfreev(tok_str_active); // g_dataset_destroy(&datalist); return; } /** * **/ void dlist_test_func(GQuark key_id,gpointer data,gpointer user_data) { if (key_id == 0) { return; } GNode *node = (GNode*)user_data; struct VyattaNode *vn = (struct VyattaNode*)node->children->data; struct VyattaNode *vn_parent = (struct VyattaNode*)node->data; struct VyattaNode *new_vn = NULL; //single entry has alread been created. if (vn->_data._value == TRUE) { new_vn = copy_vyatta_node(vn); GNode *new_node = g_node_new(new_vn); //g_node_insert(node, -1, new_node); insert_sibling_in_order(node,new_node); new_vn->_config._def = vn->_config._def; } else { new_vn = vn; strcat(new_vn->_data._path,"/value:"); if (vn_parent->_config._def.multi == FALSE) { char *tmp = (char*)g_quark_to_string(key_id); char *slash = strchr(tmp,'/'); if (slash == NULL) { strcat(new_vn->_data._path,tmp); } else { do { //escape '/' to %2F strncat(new_vn->_data._path,tmp,slash - tmp); strncat(new_vn->_data._path,"%2F",3); ++slash; tmp = slash; } while ((slash = strchr(slash,'/')) != NULL); strcat(new_vn->_data._path,tmp); } } } new_vn->_data._value = TRUE; strcpy(new_vn->_data._name,(char*)g_quark_to_string(key_id)); new_vn->_config._path = malloc(MAX_LENGTH_DIR_PATH*sizeof(char)); sprintf(new_vn->_config._path,"%s/node.tag",vn_parent->_config._path); new_vn->_data._operation = ((struct ValueData*)data)->_state; new_vn->_config._def = vn_parent->_config._def; } /** * **/ GNode* insert_sibling_in_order(GNode *parent, GNode *child) { //find alphabetical order to insert child into sibling GNode *sibling = parent->children; while (sibling != NULL) { if (strcmp((((struct VyattaNode*)(child->data))->_data._name),((struct VyattaNode*)(sibling->data))->_data._name) > 0) { break; } sibling = sibling->next; } GNode *new_node = g_node_insert_after(parent, sibling, child); return new_node; } /** * **/ static void piecewise_copy(GNode *root_node, boolean test_mode) { struct SrcDst sd; sd._src = get_tmpp(); //copy of merged config sd._dst = get_adirp(); //active config sd._test_mode = test_mode; //COPY FROM TOP DOWN g_node_traverse(root_node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, (GNodeTraverseFunc)copy_func, (gpointer)&sd); //delete needs to apply to changes only as src sd._src = get_cdirp(); //changes only config //DELETE FROM BOTTOM UP, stop on finding children g_node_traverse(root_node, G_POST_ORDER, G_TRAVERSE_ALL, -1, (GNodeTraverseFunc)delete_func, (gpointer)&sd); } /** * * **/ static gboolean copy_func(GNode *node, gpointer data) { char buf[MAX_LENGTH_DIR_PATH]; if (node == NULL) { return FALSE; } struct SrcDst *sd = (struct SrcDst*)data; char *path = ((struct VyattaNode*)(node->data))->_data._path; //might not work for terminating multinodes as the node.val won't be copied if (((struct VyattaNode*)(node->data))->_data._value == TRUE && ((struct VyattaNode*)(node->data))->_config._def.tag == FALSE) { char *parent_path; char buf1[MAX_LENGTH_DIR_PATH]; //THIS IS ONLY FOR NODE.VAL (or leafs, term multis) //before copy also need to clear out def file in active directory (will copy over current if found) //this is for the case where it is set by default, then unset at the node--i.e. no longer a default value. if (((struct VyattaNode*)(node->data))->_config._multi == FALSE) { //only for leaf char *parent_path = ((struct VyattaNode*)(node->parent->data))->_data._path; if (g_debug) { printf("rm %s%sdef\n", sd->_dst, parent_path); syslog(LOG_DEBUG, "rm %s%sdef", sd->_dst, parent_path); fflush(NULL); } if (sd->_test_mode == FALSE) { if (snprintf(buf, MAX_LENGTH_DIR_PATH, "%s%sdef", sd->_dst, parent_path) < MAX_LENGTH_DIR_PATH) { sys_rm(buf); } } } parent_path = ((struct VyattaNode*)(node->parent->data))->_data._path; if (g_debug) { printf("cp %s%s{node.val,def} %s%s\n", sd->_src, parent_path, sd->_dst, parent_path); syslog(LOG_DEBUG, "cp %s%s{node.val,def} %s%s\n", sd->_src, parent_path, sd->_dst, parent_path); fflush(NULL); } if (sd->_test_mode == FALSE) { if (snprintf(buf, MAX_LENGTH_DIR_PATH, "%s%snode.val", sd->_src, parent_path) < MAX_LENGTH_DIR_PATH && snprintf(buf1, MAX_LENGTH_DIR_PATH, "%s%snode.val", sd->_dst, parent_path) < MAX_LENGTH_DIR_PATH) { sys_cp(buf, buf1); } if (snprintf(buf, MAX_LENGTH_DIR_PATH, "%s%sdef", sd->_src, parent_path) < MAX_LENGTH_DIR_PATH && snprintf(buf1, MAX_LENGTH_DIR_PATH, "%s%sdef", sd->_dst, parent_path) < MAX_LENGTH_DIR_PATH) { sys_cp(buf, buf1); } } } else { if (!IS_DELETE(((struct VyattaNode*)(node->data))->_data._operation)) { if (g_debug) { printf("mkdir_p %s%s\n", sd->_dst, path); syslog(LOG_DEBUG, "mkdir_p %s%s", sd->_dst, path); fflush(NULL); } if (sd->_test_mode == FALSE) { if (snprintf(buf, MAX_LENGTH_DIR_PATH, "%s%s", sd->_dst, path) < MAX_LENGTH_DIR_PATH) { sys_mkdir_p(buf); } } } } return FALSE; } /** * * **/ static gboolean delete_func(GNode *node, gpointer data) { if (node == NULL) { return FALSE; } struct SrcDst *sd = (struct SrcDst*)data; char *command = malloc(MAX_LENGTH_DIR_PATH); //DONT HAVE THE COMMAND BELOW BLOW AWAY WHITEOUT FILES!!!!! static const char format[]="rm -f %s%s{*,.*} >&/dev/null;rmdir %s%s >&/dev/null ; /bin/true"; //need to remove opaque file. static const char format_force_delete[]="rm -f %s%s{*,.*} >&/dev/null;rmdir %s%s >&/dev/null ; /bin/true"; //force delete as this is a delete operation with dependency static const char delete_format[]="rm %s%s../.wh.%s >&/dev/null"; char *path = ((struct VyattaNode*)(node->data))->_data._path; //does this node have any children that have not been copied???? //NEED RM -FV on changes only directory!!!! for normal removal!!! //WILL ONLY REMOVE DIRS WITHOUT CHILD DIRS--just what we want.. //NEED TO PREVENT THE COMMAND BELOW FROM DELETING WHITEOUT FILES.... if (IS_NOOP(((struct VyattaNode*)(node->data))->_data._operation)) { return FALSE; //see if we can skip this node here } //DOESN'T QUITE FIX THE PROBLEM, THE PARENT IS CALLED (AND PROBABLY SHOULDN'T BE) if (!IS_DELETE(((struct VyattaNode*)(node->data))->_data._operation)) { sprintf(command,format,sd->_src,path,sd->_src,path); if (g_debug) { printf("%s\n",command); syslog(LOG_DEBUG,"%s\n",command); fflush(NULL); } if (sd->_test_mode == FALSE) { system(command); } } //if this is a deletion operation, need to remove if (IS_DELETE(((struct VyattaNode*)(node->data))->_data._operation) && !IS_ACTIVE(((struct VyattaNode*)(node->data))->_data._operation)) { //DO NOT PERFORM THIS STEP IF THERE ARE SUBDIRECTORIES (only the whiteout file) //remove .whiteout file in c directory if encountered in walk. sprintf(command,delete_format,sd->_src,path,((struct VyattaNode*)(node->data))->_data._name); if (g_debug) { printf("%s\n",command); syslog(LOG_DEBUG,"%s\n",command); fflush(NULL); } if (sd->_test_mode == FALSE) { system(command); } //if delete then remove entry in active configuration! sprintf(command,format_force_delete,sd->_dst,path,sd->_dst,path); if (g_debug) { printf("%s\n",command); syslog(LOG_DEBUG,"%s\n",command); fflush(NULL); } if (sd->_test_mode == FALSE) { system(command); } } free(command); return FALSE; } /** * * **/ static gboolean delete_wh_func(GNode *node, gpointer data) { if (node == NULL) { return FALSE; } char abuf[MAX_LENGTH_DIR_PATH]; static const char format0[]="rm -fr %s >&/dev/null ; /bin/true"; struct SrcDst *sd = (struct SrcDst*)data; GNode *parent_node = node->parent; //on node where operation is delete and parent is noop or active then remove directory from active config //if this is a deletion operation, need to remove if (parent_node != NULL) { if (IS_DELETE(((struct VyattaNode*)(node->data))->_data._operation) && !IS_ACTIVE(((struct VyattaNode*)(node->data))->_data._operation) && !IS_DELETE(((struct VyattaNode*)(parent_node->data))->_data._operation)) { char *path = ((struct VyattaNode*)(node->data))->_data._path; sprintf(abuf,"%s%s",get_adirp(),path); //mkdir temp merge char command[MAX_LENGTH_DIR_PATH]; sprintf(command,format0,abuf); if (g_debug) { printf("%s\n",command); syslog(LOG_DEBUG,"%s\n",command); fflush(NULL); } if (sd->_test_mode == FALSE) { system(command); } } } else { if (IS_DELETE(((struct VyattaNode*)(node->data))->_data._operation) && !IS_ACTIVE(((struct VyattaNode*)(node->data))->_data._operation)) { char *path = ((struct VyattaNode*)(node->data))->_data._path; sprintf(abuf,"%s%s",get_adirp(),path); //mkdir temp merge char command[MAX_LENGTH_DIR_PATH]; sprintf(command,format0,abuf); if (g_debug) { printf("%s\n",command); syslog(LOG_DEBUG,"%s\n",command); fflush(NULL); } if (sd->_test_mode == FALSE) { system(command); } } } return FALSE; }