/* Prepare metadata */ int prepare_metadata(uint32_t metaflags, struct collection_item **metadata, int *save_error) { int error = EOK; struct collection_item *metasec = NULL; TRACE_FLOW_STRING("prepare_metadata", "Entry"); /* Are we supposed to collect or process meta data ? */ if (!metadata) { TRACE_FLOW_STRING("No meta data", "Exit"); return EOK; } /* Allocate metadata */ error = col_create_collection(metadata, INI_METADATA, COL_CLASS_INI_META); if (error) { TRACE_ERROR_NUMBER("Failed to create meta data", error); return error; } /* Check and create section for file error if needed */ if (metaflags & INI_META_SEC_ERROR_FLAG) { /* Create ERROR collection */ if ((error = col_create_collection(&metasec, INI_META_SEC_ERROR, COL_CLASS_INI_SECTION)) || (error = col_add_collection_to_collection( *metadata, NULL, NULL, metasec, COL_ADD_MODE_REFERENCE))) { TRACE_ERROR_NUMBER("Failed to create error section", error); col_destroy_collection(metasec); col_destroy_collection(*metadata); *metadata = NULL; return error; } /* If we are here we would have to save file open error */ *save_error = 1; col_destroy_collection(metasec); } TRACE_FLOW_STRING("prepare_metadata", "Exit"); return error; }
/* Function that creates a stack object */ int col_create_stack(struct collection_item **stack) { int error = EOK; TRACE_FLOW_STRING("col_create_stack", "Entry point."); error = col_create_collection(stack, COL_NAME_STACK, COL_CLASS_STACK); TRACE_FLOW_STRING("col_create_stack", "Exit."); return error; }
/* Create a config object */ int ini_config_create(struct ini_cfgobj **ini_config) { int error = EOK; struct ini_cfgobj *new_co = NULL; TRACE_FLOW_ENTRY(); if (!ini_config) { TRACE_ERROR_NUMBER("Invalid argument", EINVAL); return EINVAL; } errno = 0; new_co = malloc(sizeof(struct ini_cfgobj)); if (!new_co) { error = errno; TRACE_ERROR_NUMBER("Failed to allocate memory", ENOMEM); return ENOMEM; } new_co->cfg = NULL; new_co->boundary = INI_WRAP_BOUNDARY; /* Create a collection to hold configuration data */ error = col_create_collection(&(new_co->cfg), INI_CONFIG_NAME, COL_CLASS_INI_CONFIG); if (error != EOK) { TRACE_ERROR_NUMBER("Failed to create collection.", error); ini_config_destroy(new_co); return error; } *ini_config = new_co; TRACE_FLOW_EXIT(); return error; }
/* Complete value processing */ static int complete_value_processing(struct parser_obj *po) { int error = EOK; int error2 = EOK; struct value_obj *vo = NULL; struct value_obj *vo_old = NULL; unsigned insertmode; uint32_t mergemode; int suppress = 0; int doinsert = 0; struct collection_item *item = NULL; struct collection_item *section = NULL; int merging = 0; TRACE_FLOW_ENTRY(); if (po->merge_sec) { TRACE_INFO_STRING("Processing value in merge mode", ""); section = po->merge_sec; merging = 1; } else if(!(po->sec)) { TRACE_INFO_STRING("Creating default section", ""); /* If there is not open section create a default one */ error = col_create_collection(&po->sec, INI_DEFAULT_SECTION, COL_CLASS_INI_SECTION); if (error) { TRACE_ERROR_NUMBER("Failed to create default section", error); return error; } section = po->sec; } else { TRACE_INFO_STRING("Processing value in normal mode", ""); section = po->sec; } if (merging) { TRACE_INFO_STRING("Using merge key:", po->merge_key); vo = po->merge_vo; /* We are adding to the merge section so use MV2S flags. * But flags are done in such a way that deviding MV2S by MV1S mask * will translate MV2S flags into MV1S so we can use * MV1S constants. */ TRACE_INFO_NUMBER("Collisions flags:", po->collision_flags); mergemode = (po->collision_flags & INI_MV2S_MASK) / INI_MV1S_MASK; } else { /* Construct value object from what we have */ error = value_create_from_refarray(po->raw_lines, po->raw_lengths, po->keylinenum, INI_VALUE_READ, po->key_len, po->boundary, po->ic, &vo); if (error) { TRACE_ERROR_NUMBER("Failed to create value object", error); return error; } /* Forget about the arrays. They are now owned by the value object */ po->ic = NULL; po->raw_lines = NULL; po->raw_lengths = NULL; mergemode = po->collision_flags & INI_MV1S_MASK; } switch (mergemode) { case INI_MV1S_ERROR: insertmode = COL_INSERT_DUPERROR; doinsert = 1; break; case INI_MV1S_PRESERVE: insertmode = COL_INSERT_DUPERROR; doinsert = 1; suppress = 1; break; case INI_MV1S_ALLOW: insertmode = COL_INSERT_NOCHECK; doinsert = 1; break; case INI_MV1S_OVERWRITE: /* Special handling */ case INI_MV1S_DETECT: default: break; } /* Do not insert but search for dups first */ if (!doinsert) { TRACE_INFO_STRING("Overwrite mode. Looking for:", (char *)(merging ? po->merge_key : po->key)); error = col_get_item(section, merging ? po->merge_key : po->key, COL_TYPE_BINARY, COL_TRAVERSE_DEFAULT, &item); if (error) { TRACE_ERROR_NUMBER("Failed searching for dup", error); value_destroy(vo); return error; } /* Check if there is a dup */ if (item) { /* Check if we are in the detect mode */ if (mergemode == INI_MV1S_DETECT) { po->merge_error = EEXIST; /* There is a dup - inform user about it and continue */ error = save_error(po->el, merging ? po->seclinenum : po->keylinenum, merging ? ERR_DUPKEYSEC : ERR_DUPKEY, ERROR_TXT); if (error) { TRACE_ERROR_NUMBER("Failed to save error", error); value_destroy(vo); return error; } doinsert = 1; insertmode = COL_INSERT_NOCHECK; } else { /* Dup exists - update it */ vo_old = *((struct value_obj **)(col_get_item_data(item))); error = col_modify_binary_item(item, NULL, &vo, sizeof(struct value_obj *)); if (error) { TRACE_ERROR_NUMBER("Failed updating the value", error); value_destroy(vo); return error; } /* If we failed to update it is better to leak then crash, * so destroy original value only on the successful update. */ value_destroy(vo_old); } } else { /* No dup found so we can insert with no check */ doinsert = 1; insertmode = COL_INSERT_NOCHECK; } } if (doinsert) { /* Add value to collection */ error = col_insert_binary_property(section, NULL, COL_DSP_END, NULL, 0, insertmode, merging ? po->merge_key : po->key, &vo, sizeof(struct value_obj *)); if (error) { value_destroy(vo); if ((suppress) && (error == EEXIST)) { TRACE_INFO_STRING("Preseved exisitng value", (char *)(merging ? po->merge_key : po->key)); } else { /* Check if this is a critical error or not */ if ((mergemode == INI_MV1S_ERROR) && (error == EEXIST)) { TRACE_ERROR_NUMBER("Failed to add value object " "to the section", error); error2 = save_error(po->el, merging ? po->seclinenum : po->keylinenum, merging ? ERR_DUPKEYSEC : ERR_DUPKEY, ERROR_TXT); if (error2) { TRACE_ERROR_NUMBER("Failed to save error", error2); return error2; } return error; } else { TRACE_ERROR_NUMBER("Failed to add value object" " to the section", error); return error; } } } } if (!merging) { free(po->key); po->key = NULL; po->key_len = 0; } TRACE_FLOW_EXIT(); return EOK; }
/* Create parse object * * It assumes that the ini collection * has been precreated. */ static int parser_create(struct ini_cfgobj *co, FILE *file, const char *config_filename, int error_level, uint32_t collision_flags, uint32_t parse_flags, struct parser_obj **po) { int error = EOK; struct parser_obj *new_po = NULL; unsigned count = 0; TRACE_FLOW_ENTRY(); /* Make sure that all the parts are initialized */ if ((!po) || (!co) || (!(co->cfg)) || (!file) || (!config_filename)) { TRACE_ERROR_NUMBER("Invalid argument", EINVAL); return EINVAL; } error = col_get_collection_count(co->cfg, &count); if (error) { TRACE_ERROR_NUMBER("Failed to check object size", error); return error; } if (count != 1) { TRACE_ERROR_NUMBER("Configuration is not empty", EINVAL); return EINVAL; } new_po = malloc(sizeof(struct parser_obj)); if (!new_po) { TRACE_ERROR_NUMBER("No memory", ENOMEM); return ENOMEM; } /* Save external data */ new_po->file = file; new_po->el = co->error_list; new_po->filename = config_filename; new_po->error_level = error_level; new_po->collision_flags = collision_flags; new_po->parse_flags = parse_flags; new_po->boundary = co->boundary; new_po->co = co; /* Initialize internal varibles */ new_po->sec = NULL; new_po->merge_sec = NULL; new_po->ic = NULL; new_po->last_error = 0; new_po->linenum = 0; new_po->keylinenum = 0; new_po->seclinenum = 0; new_po->last_read = NULL; new_po->last_read_len = 0; new_po->inside_comment = 0; new_po->key = NULL; new_po->key_len = 0; new_po->raw_lines = NULL; new_po->raw_lengths = NULL; new_po->ret = EOK; new_po->merge_key = NULL; new_po->merge_vo = NULL; new_po->merge_error = 0; new_po->top = NULL; new_po->queue = NULL; /* Create top collection */ error = col_create_collection(&(new_po->top), INI_CONFIG_NAME, COL_CLASS_INI_CONFIG); if (error) { TRACE_ERROR_NUMBER("Failed to create top collection", error); parser_destroy(new_po); return error; } /* Create a queue */ error = col_create_queue(&(new_po->queue)); if (error) { TRACE_ERROR_NUMBER("Failed to create queue", error); parser_destroy(new_po); return error; } error = col_enqueue_unsigned_property(new_po->queue, PARSE_ACTION, PARSE_READ); if (error) { TRACE_ERROR_NUMBER("Failed to create queue", error); parser_destroy(new_po); return error; } *po = new_po; TRACE_FLOW_EXIT(); return error; }
/* Parse and process section */ static int handle_section(struct parser_obj *po, uint32_t *action) { int error = EOK; char *start; char *end; char *dupval; uint32_t len; TRACE_FLOW_ENTRY(); /* We are safe to substract 1 * since we know that there is at * least one character on the line * based on the check above. */ end = po->last_read + po->last_read_len - 1; while (isspace(*end)) end--; if (*end != ']') { *action = PARSE_ERROR; po->last_error = ERR_NOCLOSESEC; return EOK; } /* Skip spaces at the beginning of the section name */ start = po->last_read + 1; while (isspace(*start)) start++; /* Check if there is a section name */ if (start == end) { *action = PARSE_ERROR; po->last_error = ERR_NOSECTION; return EOK; } /* Skip spaces at the end of the section name */ end--; while (isspace(*end)) end--; /* We got section name */ len = end - start + 1; if (len > MAX_KEY) { *action = PARSE_ERROR; po->last_error = ERR_SECTIONLONG; return EOK; } if (po->key) { /* Complete processing of the previous value */ error = complete_value_processing(po); if (error) { TRACE_ERROR_NUMBER("Failed to complete value processing", error); return error; } } /* Save section if we have one*/ error = parser_save_section(po); if (error) { TRACE_ERROR_NUMBER("Failed to save section", error); return error; } /* Dup the name */ dupval = malloc(len + 1); if (!dupval) { TRACE_ERROR_NUMBER("Failed to dup section name", ENOMEM); return ENOMEM; } memcpy(dupval, start, len); dupval[len] = '\0'; /* Create a new section */ error = col_create_collection(&po->sec, dupval, COL_CLASS_INI_SECTION); if (error) { TRACE_ERROR_NUMBER("Failed to create a section", error); free(dupval); return error; } /* But if there is just a comment then create a special key */ po->key_len = sizeof(INI_SECTION_KEY) - 1; po->key = strndup(INI_SECTION_KEY, sizeof(INI_SECTION_KEY)); /* Create new arrays */ error = value_create_arrays(&(po->raw_lines), &(po->raw_lengths)); if (error) { TRACE_ERROR_NUMBER("Failed to create arrays", error); free(dupval); return error; } /* Save a duplicated part in the value */ error = value_add_to_arrays(dupval, len, po->raw_lines, po->raw_lengths); if (error) { TRACE_ERROR_NUMBER("Failed to add value to the arrays", error); free(dupval); return error; } /* Save the line number of the last found key */ po->seclinenum = po->linenum; /* Complete processing of this value. * A new section will be created inside and a special * value will be added. */ error = complete_value_processing(po); if (error) { TRACE_ERROR_NUMBER("Failed to complete value processing", error); return error; } /* We are done dealing with section */ free(po->last_read); po->last_read = NULL; po->last_read_len = 0; *action = PARSE_READ; TRACE_FLOW_EXIT(); return EOK; }
/* Collect metadata for the file */ int collect_metadata(uint32_t metaflags, struct collection_item **metadata, FILE *config_file, const char *config_filename) { int error = EOK; struct collection_item *metasec = NULL; int filedes; struct stat file_stats; char buff[CONVERSION_BUFFER]; TRACE_FLOW_STRING("collect_metadata", "Entry"); /* Check and create section for file error if needed */ if (metaflags & INI_META_SEC_ACCESS_FLAG) { /* Create ACCESS collection */ error = col_create_collection(&metasec, INI_META_SEC_ACCESS, COL_CLASS_INI_SECTION); if (error) { TRACE_ERROR_NUMBER("Failed to create access section.", error); col_destroy_collection(metasec); return error; } filedes = fileno(config_file); /* Collect statistics */ errno = 0; if (fstat(filedes, &file_stats) < 0) { error = errno; TRACE_ERROR_NUMBER("Failed to get statistics.", error); col_destroy_collection(metasec); return error; } /* Record statistics */ /* UID */ snprintf(buff, CONVERSION_BUFFER, "%lu", (unsigned long)file_stats.st_uid); error = col_add_str_property(metasec, NULL, INI_META_KEY_UID, buff, 0); if (error) { TRACE_ERROR_NUMBER("Failed to save uid", error); col_destroy_collection(metasec); return error; } /* GID */ snprintf(buff, CONVERSION_BUFFER, "%lu", (unsigned long)file_stats.st_gid); error = col_add_str_property(metasec, NULL, INI_META_KEY_GID, buff, 0); if (error) { TRACE_ERROR_NUMBER("Failed to save gid", error); col_destroy_collection(metasec); return error; } /* PERMISSIONS */ snprintf(buff, CONVERSION_BUFFER, "%lu", (unsigned long)file_stats.st_mode); error = col_add_str_property(metasec, NULL, INI_META_KEY_PERM, buff, 0); if (error) { TRACE_ERROR_NUMBER("Failed to save permissions", error); col_destroy_collection(metasec); return error; } /* Modification time stamp */ snprintf(buff, CONVERSION_BUFFER, "%ld", (long int)file_stats.st_mtime); error = col_add_str_property(metasec, NULL, INI_META_KEY_MODIFIED, buff, 0); if (error) { TRACE_ERROR_NUMBER("Failed to save modification time", error); col_destroy_collection(metasec); return error; } /* Name */ error = col_add_str_property(metasec, NULL, INI_META_KEY_NAME, config_filename, 0); if (error) { TRACE_ERROR_NUMBER("Failed to save file name", error); col_destroy_collection(metasec); return error; } /* The device ID can actualy be bigger than * 32-bits according to the type sizes. * However it is probaly not going to happen * on a real system. * Add a check for this case. */ if (file_stats.st_dev > ULONG_MAX) { TRACE_ERROR_NUMBER("Device is out of range", ERANGE); col_destroy_collection(metasec); return ERANGE; } /* Device ID */ TRACE_INFO_LNUMBER("Device ID", file_stats.st_dev); snprintf(buff, CONVERSION_BUFFER, "%lu", (unsigned long)file_stats.st_dev); error = col_add_str_property(metasec, NULL, INI_META_KEY_DEV, buff, 0); if (error) { TRACE_ERROR_NUMBER("Failed to save inode", error); col_destroy_collection(metasec); return error; } /* i-node */ snprintf(buff, CONVERSION_BUFFER, "%lu", (unsigned long)file_stats.st_ino); error = col_add_str_property(metasec, NULL, INI_META_KEY_INODE, buff, 0); if (error) { TRACE_ERROR_NUMBER("Failed to save inode", error); col_destroy_collection(metasec); return error; } /* Add section to metadata */ error = col_add_collection_to_collection( *metadata, NULL, NULL, metasec, COL_ADD_MODE_REFERENCE); col_destroy_collection(metasec); if (error) { TRACE_ERROR_NUMBER("Failed to save file name", error); return error; } } TRACE_FLOW_STRING("collect_metadata", "Exit"); return error; }