/* SPDX-License-Identifier: GPL-2.0 */ /** \file attribute.c Attribute handling functions. */ #include #include #include "server_cmd.lsg.h" #include "para.h" #include "error.h" #include "crypt.h" #include "string.h" #include "afh.h" #include "afs.h" #include "ipc.h" #include "sideband.h" #include "command.h" static struct osl_table *attribute_table; /** The columns of the attribute table. */ enum attribute_table_columns { /** The bit number (0-63). */ ATTCOL_BITNUM, /** The name of the attribute. */ ATTCOL_NAME, /** Number of columns in this table. */ NUM_ATT_COLUMNS }; static int u8_compare(const struct osl_object *obj1, const struct osl_object *obj2) { uint8_t c1 = *(const uint8_t *)obj1->data; uint8_t c2 = *(const uint8_t *)obj2->data; if (c1 > c2) return 1; if (c1 < c2) return -1; return 0; } static struct osl_column_description att_cols[] = { [ATTCOL_BITNUM] = { .storage_type = OSL_MAPPED_STORAGE, .storage_flags = OSL_RBTREE | OSL_FIXED_SIZE | OSL_UNIQUE, .name = "bitnum", .compare_function = u8_compare, .data_size = 1 }, [ATTCOL_NAME] = { .storage_type = OSL_MAPPED_STORAGE, .storage_flags = OSL_RBTREE | OSL_UNIQUE, .name = "name", .compare_function = string_compare, } }; static struct osl_table_description attribute_table_desc = { .name = "attributes", .num_columns = NUM_ATT_COLUMNS, .flags = 0, .column_descriptions = att_cols }; /** * Retrieve the bit number of the named attribute. * * \param att_name The name of the attribute. * * \return The attribute number of success, negative on errors. */ int attr_name_to_bitnum(const char *att_name) { struct osl_object obj = {.data = (char *)att_name, .size = strlen(att_name) + 1}; struct osl_row *row; int ret = osl(osl_get_row(attribute_table, ATTCOL_NAME, &obj, &row)); if (ret < 0) return ret; ret = osl(osl_get_object(attribute_table, row, ATTCOL_BITNUM, &obj)); if (ret < 0) return ret; return *(uint8_t *)obj.data; } /* Data passed to the action function of lsatt */ static int print_attribute(struct osl_table *table, struct osl_row *row, const char *name, void *data) { struct afs_callback_arg *aca = data; bool l_given = SERVER_CMD_OPT_GIVEN(LSATT, LONG, aca->lpr); struct osl_object bitnum_obj; int ret; if (!l_given) { para_printf(&aca->pbout, "%s\n", name); return 1; } ret = osl(osl_get_object(table, row, ATTCOL_BITNUM, &bitnum_obj)); if (ret < 0) { afs_error(aca, "%s: %s\n", name, para_strerror(-ret)); return ret; } para_printf(&aca->pbout, "%u\t%s\n", *(uint8_t *)bitnum_obj.data, name); return 1; } static int com_lsatt_callback(struct afs_callback_arg *aca) { const struct lls_command *cmd = SERVER_CMD_CMD_PTR(LSATT); bool i_given, r_given; int ret; struct pattern_match_data pmd = { .table = attribute_table, .loop_col_num = ATTCOL_NAME, .match_col_num = ATTCOL_NAME, .pm_flags = PM_NO_PATTERN_MATCHES_EVERYTHING, .data = aca, .action = print_attribute }; ret = lls(lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr)); assert(ret >= 0); pmd.lpr = aca->lpr; i_given = SERVER_CMD_OPT_GIVEN(LSATT, ID_SORT, aca->lpr); r_given = SERVER_CMD_OPT_GIVEN(LSATT, REVERSE, aca->lpr); if (i_given) pmd.loop_col_num = ATTCOL_BITNUM; if (r_given) pmd.pm_flags |= PM_REVERSE_LOOP; ret = for_each_matching_row(&pmd); if (ret < 0) goto out; if (pmd.num_matches == 0) ret = -E_NO_MATCH; out: lls_free_parse_result(aca->lpr, cmd); return ret; } static int com_lsatt(struct command_context *cc, struct lls_parse_result *lpr) { const struct lls_command *cmd = SERVER_CMD_CMD_PTR(LSATT); return send_lls_callback_request(com_lsatt_callback, cc->afs_fd, cmd, lpr, cc); } EXPORT_SERVER_CMD_HANDLER(lsatt); static int set_bit(struct osl_row *row, void *data) { uint64_t *defined_attributes = data; struct osl_object bitnum_obj; int ret = osl_get_object(attribute_table, row, ATTCOL_BITNUM, &bitnum_obj); if (ret < 0) return ret; *defined_attributes |= 1ULL << *(unsigned char *)bitnum_obj.data; return 1; } /** * Compute the mask of all bits which correspond to a defined attribute. * * \param result The bitmask is returned here. * * \return Standard. */ int attr_get_defined_mask(uint64_t *result) { uint64_t defined_attributes = 0; int ret = osl(osl_rbtree_loop(attribute_table, ATTCOL_BITNUM, &defined_attributes, set_bit)); if (ret < 0) return ret; *result = defined_attributes; return 1; } static int com_addatt_callback(struct afs_callback_arg *aca) { const struct lls_command *cmd = SERVER_CMD_CMD_PTR(ADDATT); int i, ret = 1; size_t len; uint64_t defined_attributes; unsigned num_inputs, num_added = 0; ret = attr_get_defined_mask(&defined_attributes); if (ret < 0) return ret; ret = lls(lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr)); assert(ret >= 0); num_inputs = lls_num_inputs(aca->lpr); for (i = 0; i < num_inputs; i++) { const char *name = lls_input(i, aca->lpr); struct osl_object objs[NUM_ATT_COLUMNS]; uint8_t bitnum; len = strlen(name); if (len == 0) { afs_error(aca, "empty attribute name\n"); ret = -ERRNO_TO_PARA_ERROR(EINVAL); goto out; } ret = attr_name_to_bitnum(name); if (ret >= 0) { afs_error(aca, "attribute already exists\n"); ret = -ERRNO_TO_PARA_ERROR(EINVAL); goto out; } if (ret != osl(-E_OSL_RB_KEY_NOT_FOUND)) { afs_error(aca, "cannot get bit number\n"); goto out; } /* find smallest unused attribute */ for (bitnum = 0; bitnum < 64; bitnum++) if (((1ULL << bitnum) & defined_attributes) == 0) break; if (bitnum == 64) { ret = -E_ATT_TABLE_FULL; goto out; } objs[ATTCOL_BITNUM].data = &bitnum; objs[ATTCOL_BITNUM].size = 1; objs[ATTCOL_NAME].data = (char *)name; objs[ATTCOL_NAME].size = len + 1; ret = osl(osl_add_row(attribute_table, objs)); if (ret < 0) goto out; num_added++; defined_attributes |= 1ULL << bitnum; } ret = 1; out: if (num_added > 0) { int ret2 = afs_event(ATTRIBUTE_ADD, NULL); if (ret2 < 0) afs_error(aca, "%s\n", para_strerror(-ret2)); } if (ret < 0) afs_error(aca, "cannot add \"%s\"\n", lls_input(i, aca->lpr)); lls_free_parse_result(aca->lpr, cmd); return ret; } static int com_addatt(struct command_context *cc, struct lls_parse_result *lpr) { const struct lls_command *cmd = SERVER_CMD_CMD_PTR(ADDATT); char *errctx; int ret = lls(lls_check_arg_count(lpr, 1, 64, &errctx)); if (ret < 0) { send_errctx(cc, errctx); return ret; } return send_lls_callback_request(com_addatt_callback, cc->afs_fd, cmd, lpr, cc); } EXPORT_SERVER_CMD_HANDLER(addatt); static int com_mvatt_callback(struct afs_callback_arg *aca) { const struct lls_command *cmd = SERVER_CMD_CMD_PTR(MVATT); const char *old, *new; struct osl_object obj; struct osl_row *row; int ret; ret = lls(lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr)); assert(ret >= 0); old = lls_input(0, aca->lpr); new = lls_input(1, aca->lpr); obj.data = (char *)old; obj.size = strlen(old) + 1; ret = osl(osl_get_row(attribute_table, ATTCOL_NAME, &obj, &row)); if (ret < 0) goto out; obj.data = (char *)new; obj.size = strlen(new) + 1; /* The update fails if the destination attribute exists. */ ret = osl(osl_update_object(attribute_table, row, ATTCOL_NAME, &obj)); out: if (ret < 0) afs_error(aca, "cannot rename %s to %s\n", old, new); else ret = afs_event(ATTRIBUTE_RENAME, NULL); lls_free_parse_result(aca->lpr, cmd); return ret; } static int com_mvatt(struct command_context *cc, struct lls_parse_result *lpr) { const struct lls_command *cmd = SERVER_CMD_CMD_PTR(MVATT); char *errctx; int ret = lls(lls_check_arg_count(lpr, 2, 2, &errctx)); if (ret < 0) { send_errctx(cc, errctx); return ret; } return send_lls_callback_request(com_mvatt_callback, cc->afs_fd, cmd, lpr, cc); } EXPORT_SERVER_CMD_HANDLER(mvatt); struct rmatt_loop_callback_data { struct afs_callback_arg *aca; const char *name; uint8_t bitnum; }; static int check_bit(struct osl_row *aft_row, void *data) { struct rmatt_loop_callback_data *rlcd = data; struct afs_info afsi; int ret = get_afsi_of_row(aft_row, &afsi); if (ret < 0) return ret; if (afsi.attributes & (1ULL << rlcd->bitnum)) { afs_error(rlcd->aca, "attribute %s still in use\n", rlcd->name); return -ERRNO_TO_PARA_ERROR(EINVAL); } return 0; } static int remove_attribute(struct osl_table *table, struct osl_row *row, const char *name, void *data) { struct afs_callback_arg *aca = data; struct rmatt_loop_callback_data rlcd = {.aca = aca, .name = name}; struct osl_object obj; int ret; ret = osl_get_object(table, row, ATTCOL_BITNUM, &obj); if (ret < 0) return ret; rlcd.bitnum = *(unsigned char *)obj.data; ret = audio_file_loop(&rlcd, check_bit); if (ret < 0) return ret; ret = osl(osl_del_row(table, row)); if (ret < 0) { afs_error(aca, "cannot remove %s\n", name); return ret; } return afs_event(ATTRIBUTE_REMOVE, NULL); } static int com_rmatt_callback(struct afs_callback_arg *aca) { const struct lls_command *cmd = SERVER_CMD_CMD_PTR(RMATT); int ret; struct pattern_match_data pmd = { .table = attribute_table, .loop_col_num = ATTCOL_BITNUM, .match_col_num = ATTCOL_NAME, .data = aca, .action = remove_attribute }; ret = lls(lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr)); assert(ret >= 0); pmd.lpr = aca->lpr; ret = for_each_matching_row(&pmd); if (ret < 0) goto out; if (pmd.num_matches == 0) ret = -E_NO_MATCH; out: lls_free_parse_result(aca->lpr, cmd); return ret; } static int com_rmatt(struct command_context *cc, struct lls_parse_result *lpr) { const struct lls_command *cmd = SERVER_CMD_CMD_PTR(RMATT); char *errctx; int ret = lls(lls_check_arg_count(lpr, 1, INT_MAX, &errctx)); if (ret < 0) { send_errctx(cc, errctx); return ret; } return send_lls_callback_request(com_rmatt_callback, cc->afs_fd, cmd, lpr, cc); } EXPORT_SERVER_CMD_HANDLER(rmatt); /** * Get the highest bit number that is associated with an attribute. * * \return On success, a number between 0 and 63 is returned. If no attributes * are defined, the function returns -E_NO_ATTRIBUTES. */ int attr_get_max_bitnum(void) { struct osl_row *row; struct osl_object obj; int ret = osl(osl_rbtree_last_row(attribute_table, ATTCOL_BITNUM, &row)); if (ret == osl(-E_OSL_RB_KEY_NOT_FOUND)) /* no attributes defined */ return -E_NO_ATTRIBUTES; if (ret < 0) return ret; ret = osl(osl_get_object(attribute_table, row, ATTCOL_BITNUM, &obj)); if (ret < 0) return ret; return *(uint8_t *)obj.data; } struct print_attname_callback_arg { uint64_t atts; struct para_buffer pbout; }; static int print_attname_if_set(struct osl_row *row, void *data) { const uint64_t one = 1; uint8_t bitnum; struct print_attname_callback_arg *paca = data; struct osl_object obj; int ret; ret = osl(osl_get_object(attribute_table, row, ATTCOL_BITNUM, &obj)); if (ret < 0) return ret; bitnum = *(uint8_t *)obj.data; if (!(paca->atts & (one << bitnum))) return 0; ret = osl(osl_get_object(attribute_table, row, ATTCOL_NAME, &obj)); if (ret < 0) return ret; para_printf(&paca->pbout, "%s%s", paca->pbout.buf? " " : "", (char *)obj.data); return 1; } /** * Get a string containing the set attributes in text form. * * \param atts The attribute bitmap. * \param text Result pointer. * * If more than one bit is set in the given bitmap, the corresponding * attribute names are separated by space characters. * * \return Negative on errors. On success, a reference to the allocated * memory is returned via the text pointer argument. It is no error if no * attributes are set. In this case an empty string is allocated. */ int attr_bitmap_to_text(uint64_t *atts, char **text) { struct print_attname_callback_arg paca = {.atts = *atts}; int ret = osl(osl_rbtree_loop(attribute_table, ATTCOL_BITNUM, &paca, print_attname_if_set)); if (ret < 0) free(paca.pbout.buf); else *text = paca.pbout.buf? paca.pbout.buf : para_strdup(""); return ret; } static void attribute_close(void) { osl_close_table(attribute_table, OSL_MARK_CLEAN); attribute_table = NULL; } static int attribute_open(const char *dir) { attribute_table_desc.dir = dir; return osl(osl_open_table(&attribute_table_desc, &attribute_table)); } static int attribute_create(const char *dir) { attribute_table_desc.dir = dir; return osl(osl_create_table(&attribute_table_desc)); } /** The attribute table stores name/bitnum pairs. */ const struct afs_table_operations attr_ops = { /* no event handler */ .open = attribute_open, .close = attribute_close, .create = attribute_create, };