/** * Get the list of children of a given parent (or list of parents). * \param parent_list [in] list of parents to get the child of * \param parent_count [in] number of ids in parent list * \param attr_mask [in] required attributes for children * \param child_id_list [out] ptr to array of child ids * \param child_attr_list [out] ptr to array of child attrs * \param child_count [out] number of returned children */ int ListMgr_GetChild(lmgr_t *p_mgr, const lmgr_filter_t *p_filter, const wagon_t *parent_list, unsigned int parent_count, attr_mask_t attr_mask, wagon_t **child_id_list, attr_set_t **child_attr_list, unsigned int *child_count) { result_handle_t result; char *path = NULL; int path_len; int rc, i; GString *req = NULL; GString *fields = NULL; GString *from = NULL; GString *where = NULL; struct field_count field_cnt = {0}; struct field_count filter_cnt = {0}; table_enum query_tab = T_DNAMES; bool distinct = false; int retry_status; /* XXX: querying children from several parent cannot work, since * we need to get the paths of the children. Or we could do a * lookup into parent_list to find the right one. In the meantime, * try not to mess up the code. */ if (unlikely(parent_count != 1)) RBH_BUG("cannot get children for several parent simultaneously"); /* always request for name to build fullpath in wagon */ attr_mask_set_index(&attr_mask, ATTR_INDEX_name); fields = g_string_new(NULL); /* append fields for all tables */ if (!attr_mask_is_null(attr_mask)) { /* retrieve source info for generated fields */ add_source_fields_for_gen(&attr_mask.std); field_cnt.nb_names = attrmask2fieldlist(fields, attr_mask, T_DNAMES, DNAMES_TABLE".", "", AOF_LEADING_SEP); field_cnt.nb_main = attrmask2fieldlist(fields, attr_mask, T_MAIN, MAIN_TABLE".", "", AOF_LEADING_SEP); field_cnt.nb_annex = attrmask2fieldlist(fields, attr_mask, T_ANNEX, ANNEX_TABLE".", "", AOF_LEADING_SEP); } else { /* no returned attrs */ if (child_attr_list != NULL) *child_attr_list = NULL; } where = g_string_new(NULL); /* starts with condition on parent */ rc = append_parent_cond(p_mgr, where, parent_list, parent_count, DNAMES_TABLE"."); if (rc != DB_SUCCESS) goto free_str; /* check filters on other tables */ if (!no_filter(p_filter)) { if (unlikely(dir_filter(p_mgr, NULL, p_filter, NULL, NULL) != FILTERDIR_NONE)) { DisplayLog(LVL_MAJOR, LISTMGR_TAG, "Directory filter not supported in %s()", __func__); rc = DB_NOT_SUPPORTED; goto free_str; } else if (unlikely(func_filter(p_mgr, NULL, p_filter, T_MAIN, 0))) { DisplayLog(LVL_MAJOR, LISTMGR_TAG, "Function filter not supported in %s()", __func__); rc = DB_NOT_SUPPORTED; goto free_str; } /* There is always a filter on T_DNAMES, which is the parent condition. * Look for optional filters. */ filter_where(p_mgr, p_filter, &filter_cnt, where, AOF_LEADING_SEP | AOF_SKIP_NAME); /** @FIXME process other filters on NAMES */ } from = g_string_new(DNAMES_TABLE); /* add filter_count + field_count to build the FROM clause. * Preserve field count which is needed to interpret the result. */ filter_cnt.nb_main += field_cnt.nb_main; filter_cnt.nb_annex += field_cnt.nb_annex; filter_cnt.nb_names += field_cnt.nb_names; /* query tab is DNAMES, skip_name=true, is_first_tab=T_DNAMES */ filter_from(p_mgr, &filter_cnt, from, &query_tab, &distinct, AOF_LEADING_SEP | AOF_SKIP_NAME); /* request is always on the DNAMES table (which contains [parent_id, id] relationship */ if (distinct) req = g_string_new("SELECT DISTINCT("DNAMES_TABLE".id) as id"); else req = g_string_new("SELECT "DNAMES_TABLE".id as id"); /* build the whole request */ g_string_append_printf(req, "%s FROM %s WHERE %s", fields->str, from->str, where->str); retry: rc = db_exec_sql(&p_mgr->conn, req->str, &result); retry_status = lmgr_delayed_retry(p_mgr, rc); if (retry_status == 1) goto retry; else if (retry_status == 2) { rc = DB_RBH_SIG_SHUTDOWN; goto free_str; } else if (rc) goto free_str; /* copy result to output structures */ *child_count = db_result_nb_records(&p_mgr->conn, &result); /* allocate entry_id array */ *child_id_list = MemCalloc(*child_count, sizeof(wagon_t)); if (*child_id_list == NULL) { rc = DB_NO_MEMORY; goto free_str; } if (child_attr_list) { *child_attr_list = MemCalloc(*child_count, sizeof(attr_set_t)); if (*child_attr_list == NULL) { rc = DB_NO_MEMORY; goto array_free; } } /* Allocate a string long enough to contain the parent path and a * child name. */ path_len = strlen(parent_list[0].fullname) + RBH_NAME_MAX + 2; path = malloc(path_len); if (!path) { DisplayLog(LVL_MAJOR, LISTMGR_TAG, "Can't alloc enough memory (%d bytes)", path_len); rc = DB_NO_MEMORY; goto array_free; } for (i = 0; i < *child_count; i++) { char *res[128]; /* 128 fields per record is large enough */ rc = db_next_record(&p_mgr->conn, &result, res, sizeof(res)/sizeof(*res)); if (rc) goto array_free; /* copy id to array */ pk2entry_id(p_mgr, res[0], &((*child_id_list)[i].id)); /* copy attributes to array */ if (child_attr_list) { unsigned int shift = 1; /* first was NAMES.id */ (*child_attr_list)[i].attr_mask = attr_mask; /* first id, then dnames attrs, then main attrs, then annex attrs */ if (field_cnt.nb_names > 0) { /* shift of 1 for id */ rc = result2attrset(T_DNAMES, res + shift, field_cnt.nb_names, &((*child_attr_list)[i])); if (rc) goto array_free; shift += field_cnt.nb_names; } if (field_cnt.nb_main > 0) { /* first id, then main attrs, then annex attrs */ /* shift of 1 for id */ rc = result2attrset(T_MAIN, res + shift, field_cnt.nb_main, &((*child_attr_list)[i])); if (rc) goto array_free; shift += field_cnt.nb_main; } if (field_cnt.nb_annex > 0) { /* shift of main_attrs count */ rc = result2attrset(T_ANNEX, res + shift, field_cnt.nb_annex, &((*child_attr_list)[i])); if (rc) goto array_free; shift += field_cnt.nb_annex; } #ifdef _LUSTRE if (stripe_fields(attr_mask)) { if (get_stripe_info(p_mgr, res[0], &ATTR(&(*child_attr_list)[i], stripe_info), &ATTR(&(*child_attr_list)[i], stripe_items))) { ATTR_MASK_UNSET(&(*child_attr_list)[i], stripe_info); ATTR_MASK_UNSET(&(*child_attr_list)[i], stripe_items); } } #endif generate_fields(&((*child_attr_list)[i])); /* Note: path is properly sized already to not overflow. */ snprintf(path, path_len, "%s/%s", parent_list[0].fullname, (*child_attr_list)[i].attr_values.name); (*child_id_list)[i].fullname = strdup(path); } } if (path) free(path); db_result_free(&p_mgr->conn, &result); g_string_free(req, TRUE); g_string_free(fields, TRUE); g_string_free(from, TRUE); g_string_free(where, TRUE); return 0; array_free: if (path) free(path); if (child_attr_list && *child_attr_list) { MemFree(*child_attr_list); *child_attr_list = NULL; } MemFree(*child_id_list); *child_id_list = NULL; free_str: g_string_free(req, TRUE); g_string_free(fields, TRUE); g_string_free(from, TRUE); g_string_free(where, TRUE); return rc; }
/** * check if the entry exists in the database and what info * must be retrieved. */ int EntryProc_get_info_db( struct entry_proc_op_t *p_op, lmgr_t * lmgr ) { int rc = 0; int next_stage = -1; /* -1 = skip */ attr_mask_t attr_allow_cached = null_mask; attr_mask_t attr_need_fresh = null_mask; uint32_t status_scope = 0; /* status mask only */ attr_mask_t tmp; const pipeline_stage_t *stage_info = &entry_proc_pipeline[p_op->pipeline_stage]; /* check if entry is in policies scope */ add_matching_scopes_mask(&p_op->entry_id, &p_op->fs_attrs, true, &status_scope); /* XXX also retrieve needed attributes to check the scope? */ /* get diff attributes from DB and FS (to allow comparison) */ p_op->db_attr_need = attr_mask_or(&p_op->db_attr_need, &diff_mask); tmp = attr_mask_and_not(&diff_mask, &p_op->fs_attrs.attr_mask); p_op->fs_attr_need = attr_mask_or(&p_op->fs_attr_need, &tmp); if (entry_proc_conf.detect_fake_mtime) attr_mask_set_index(&p_op->db_attr_need, ATTR_INDEX_creation_time); attr_allow_cached = attrs_for_status_mask(status_scope, false); attr_need_fresh = attrs_for_status_mask(status_scope, true); /* XXX check if entry is in policy scope? */ /* what must be retrieved from DB: */ tmp = attr_mask_and_not(&attr_allow_cached, &p_op->fs_attrs.attr_mask); p_op->db_attr_need = attr_mask_or(&p_op->db_attr_need, &tmp); /* no dircount for non-dirs */ if (ATTR_MASK_TEST(&p_op->fs_attrs, type) && !strcmp(ATTR(&p_op->fs_attrs, type), STR_TYPE_DIR)) { attr_mask_unset_index(&p_op->db_attr_need, ATTR_INDEX_dircount); } /* no readlink for non symlinks */ if (ATTR_MASK_TEST(&p_op->fs_attrs, type)) /* likely */ { if (!strcmp(ATTR(&p_op->fs_attrs, type), STR_TYPE_LINK)) { attr_mask_set_index(&p_op->db_attr_need, ATTR_INDEX_link); /* check if it is known */ /* no stripe for symlinks */ attr_mask_unset_index(&p_op->db_attr_need, ATTR_INDEX_stripe_info); attr_mask_unset_index(&p_op->db_attr_need, ATTR_INDEX_stripe_items); } else attr_mask_unset_index(&p_op->db_attr_need, ATTR_INDEX_link); } if (!attr_mask_is_null(p_op->db_attr_need)) { p_op->db_attrs.attr_mask = p_op->db_attr_need; rc = ListMgr_Get(lmgr, &p_op->entry_id, &p_op->db_attrs); if (rc == DB_SUCCESS) { p_op->db_exists = 1; } else if (rc == DB_NOT_EXISTS ) { p_op->db_exists = 0; ATTR_MASK_INIT( &p_op->db_attrs ); } else { /* ERROR */ DisplayLog(LVL_CRIT, ENTRYPROC_TAG, "Error %d retrieving entry "DFID" from DB: %s.", rc, PFID(&p_op->entry_id), lmgr_err2str(rc)); p_op->db_exists = 0; ATTR_MASK_INIT( &p_op->db_attrs ); } } else { p_op->db_exists = ListMgr_Exists( lmgr, &p_op->entry_id ); } /* get status for all policies with a matching scope */ add_matching_scopes_mask(&p_op->entry_id, &p_op->fs_attrs, true, &p_op->fs_attr_need.status); tmp = attr_mask_and_not(&attr_need_fresh, &p_op->fs_attrs.attr_mask); p_op->fs_attr_need = attr_mask_or(&p_op->fs_attr_need, &tmp); if ( !p_op->db_exists ) { /* new entry */ p_op->db_op_type = OP_TYPE_INSERT; /* set creation time if it was not set by scan module */ if (!ATTR_MASK_TEST(&p_op->fs_attrs, creation_time)) { ATTR_MASK_SET( &p_op->fs_attrs, creation_time ); ATTR( &p_op->fs_attrs, creation_time ) = time(NULL); /* XXX min(atime,mtime,ctime)? */ } #ifdef _LUSTRE if (ATTR_MASK_TEST(&p_op->fs_attrs, type) && !strcmp(ATTR( &p_op->fs_attrs, type ), STR_TYPE_FILE ) /* only if it was not retrieved during the scan */ && !(ATTR_MASK_TEST(&p_op->fs_attrs, stripe_info) && ATTR_MASK_TEST(&p_op->fs_attrs, stripe_items))) { attr_mask_set_index(&p_op->fs_attr_need, ATTR_INDEX_stripe_info); attr_mask_set_index(&p_op->fs_attr_need, ATTR_INDEX_stripe_items); } #endif /* readlink for symlinks (if not already known) */ if (ATTR_MASK_TEST(&p_op->fs_attrs, type) && !strcmp(ATTR( &p_op->fs_attrs, type ), STR_TYPE_LINK) && !ATTR_MASK_TEST(&p_op->fs_attrs, link)) { attr_mask_set_index(&p_op->fs_attr_need, ATTR_INDEX_link); } else { attr_mask_unset_index(&p_op->fs_attr_need, ATTR_INDEX_link); } #ifdef ATTR_INDEX_status /** @FIXME RBHv3 drop old-style status reference */ if (ATTR_MASK_TEST(&p_op->fs_attrs, type) #ifdef _LUSTRE_HSM && !strcmp( ATTR(&p_op->fs_attrs, type), STR_TYPE_FILE )) #elif defined (_HSM_LITE) && (strcmp( ATTR(&p_op->fs_attrs, type), STR_TYPE_DIR ) != 0) && !p_op->extra_info.not_supp) #endif { p_op->fs_attr_need |= ATTR_MASK_status; #ifdef _HSM_LITE p_op->fs_attr_need |= (attr_need_fresh & ~p_op->fs_attrs.attr_mask); #endif } else {
/* Analyze a format string, and find the next chunk. Each argument is * transformed into its real printf type. For instance "%s" means the * size, and is stored as an large integer in the database, needs to * be displayed as an "%zu". */ static const char *extract_chunk(const char *str, struct fchunk *chunk) { int rc; chunk->directive = 0; while (*str) { if (*str != '%') { if (*str == '\\') { str++; switch (*str) { case '\\': g_string_append_c(chunk->format, '\\'); break; case 'n': g_string_append_c(chunk->format, '\n'); break; case 't': g_string_append_c(chunk->format, '\t'); break; case 'x': rc = parse_escaped_int(str+1, chunk, 16); if (rc == 0) { DisplayLog(LVL_CRIT, FIND_TAG, "Error: invalid \\x not followed by hex in format string"); return NULL; } /* need a new chunk for \0, return back to \\ */ if (rc == -1) return str - 1; str += rc; break; case 0: DisplayLog(LVL_CRIT, FIND_TAG, "Error: lone \\ at end of format string"); return NULL; default: /* check for octal value */ if (*str >= '0' && *str <= '7') { rc = parse_escaped_int(str, chunk, 8); /* need a new chunk for \0, return back to \\ */ if (rc == -1) return str - 1; str += rc - 1; /* -1 because no leading character */ break; } DisplayLog(LVL_CRIT, FIND_TAG, "Error: unrecognized escape code \\%c", *str); return NULL; } } else { g_string_append_c(chunk->format, *str); } str++; continue; } if (chunk->directive) { /* Already have a directive. Stop here. */ return str; } str++; if (*str == 0) { DisplayLog(LVL_CRIT, FIND_TAG, "Error: lone %% at end of format string"); return NULL; } /* Ignore %% as it is a valid printf directive, which saves a * chunk. */ if (*str == '%') { g_string_append(chunk->format, "%%"); str++; continue; } /* Found a new directive */ g_string_append_c(chunk->format, '%'); str = extract_field_width(str, chunk->format); if (str == NULL) { DisplayLog(LVL_CRIT, FIND_TAG, "Error: invalid length field in format string at %s", str); return NULL; } chunk->directive = *str; switch (*str) { case 'A': disp_mask.std |= ATTR_MASK_last_access; str = append_time_format(str, chunk); if (str == NULL) return NULL; break; case 'b': disp_mask.std |= ATTR_MASK_blocks; g_string_append(chunk->format, "zu"); break; case 'C': disp_mask.std |= ATTR_MASK_last_mdchange; str = append_time_format(str, chunk); if (str == NULL) return NULL; break; case 'd': disp_mask.std |= ATTR_MASK_depth; g_string_append_c(chunk->format, 'u'); break; case 'f': disp_mask.std |= ATTR_MASK_name; g_string_append_c(chunk->format, 's'); break; case 'g': disp_mask.std |= ATTR_MASK_gid; if (global_config.uid_gid_as_numbers) g_string_append_c(chunk->format, 'd'); else g_string_append_c(chunk->format, 's'); break; case 'M': disp_mask.std |= ATTR_MASK_mode; g_string_append_c(chunk->format, 's'); break; case 'm': disp_mask.std |= ATTR_MASK_mode; g_string_append_c(chunk->format, 'o'); break; case 'n': disp_mask.std |= ATTR_MASK_nlink; g_string_append_c(chunk->format, 'u'); break; case 'p': g_string_append_c(chunk->format, 's'); break; case 's': disp_mask.std |= ATTR_MASK_size; g_string_append(chunk->format, "zu"); break; case 'T': disp_mask.std |= ATTR_MASK_last_mod; str = append_time_format(str, chunk); if (str == NULL) return NULL; break; case 'u': disp_mask.std |= ATTR_MASK_uid; if (global_config.uid_gid_as_numbers) g_string_append_c(chunk->format, 'd'); else g_string_append_c(chunk->format, 's'); break; case 'Y': disp_mask.std |= ATTR_MASK_type; g_string_append_c(chunk->format, 's'); break; case 'y': disp_mask.std |= ATTR_MASK_type; g_string_append_c(chunk->format, 'c'); break; case 'R': str++; chunk->sub_directive = *str; switch (*str) { case 'C': disp_mask.std |= ATTR_MASK_creation_time; str = append_time_format(str, chunk); if (str == NULL) return NULL; break; case 'c': disp_mask.std |= ATTR_MASK_fileclass; g_string_append_c(chunk->format, 's'); break; case 'f': g_string_append_c(chunk->format, 's'); break; case 'm': /* Module name and attribute followed by * format. e.g. "%Rm{lhsm.archive_id}". */ str++; str = extract_mod_attr(str, chunk); if (str == NULL) { DisplayLog(LVL_CRIT, FIND_TAG, "Error: cannot extract module attribute name, or invalid name"); return NULL; } attr_mask_set_index(&disp_mask, chunk->attr_index); if (strcmp(chunk->def->user_name, "status") == 0) { /* status is a special case. Change the directive * for print_entry(). */ chunk->sub_directive = SUB_DIRECTIVE_STATUS; g_string_append_c(chunk->format, 's'); break; } chunk->rel_sm_info_index = attr2sminfo_index(chunk->attr_index) - chunk->smi->sm_info_offset; /* The format for that attribute */ switch (chunk->def->db_type) { case DB_UINT: case DB_BOOL: g_string_append_c(chunk->format, 'u'); break; case DB_INT: g_string_append_c(chunk->format, 'i'); break; case DB_TEXT: g_string_append_c(chunk->format, 's'); break; default: DisplayLog(LVL_CRIT, FIND_TAG, "Error: unsupported database format %d", chunk->def->db_type); break; } break; case 'o': disp_mask.std |= ATTR_MASK_stripe_items; g_string_append_c(chunk->format, 's'); break; case 'p': disp_mask.std |= ATTR_MASK_parent_id; g_string_append_c(chunk->format, 's'); break; case 0: DisplayLog(LVL_CRIT, FIND_TAG, "Error: lone %%R at end of format string"); return NULL; default: DisplayLog(LVL_CRIT, FIND_TAG, "Error: unrecognized format %%R%c", *str); return NULL; } break; case 0: DisplayLog(LVL_CRIT, FIND_TAG, "Error: lone %% at end of format string"); return NULL; default: DisplayLog(LVL_CRIT, FIND_TAG, "Error: unrecognized format %%%c", *str); return NULL; } str++; } return str; }