/* * Copyright (C) 1997-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file attribute.c Attribute handling functions. */ #include "para.h" #include "error.h" #include "string.h" #include "afh.h" #include "afs.h" #include "net.h" #include "ipc.h" static struct osl_table *attribute_table; static int greatest_att_bitnum; /** 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 char_compare(const struct osl_object *obj1, const struct osl_object *obj2) { const unsigned char *c1 = (const unsigned char*)obj1->data; const unsigned char *c2 = (const unsigned char*)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 = char_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 }; static void find_greatest_att_bitnum(void) { unsigned char c = 63; do { struct osl_row *row; struct osl_object obj = {.data = &c, .size = 1}; if (osl_get_row(attribute_table, ATTCOL_BITNUM, &obj, &row) >= 0) { greatest_att_bitnum = c; return; } } while (c--); PARA_INFO_LOG("no attributes\n"); greatest_att_bitnum = -E_NO_ATTRIBUTES; } /** * Retrieve the identifier (number) of an attribute. * * \param att_name The name of the attribute. * \param bitnum Result pointer. * * \return Positive on success, negative on errors. */ int get_attribute_bitnum_by_name(const char *att_name, unsigned char *bitnum) { struct osl_object obj = {.data = (char *)att_name, .size = strlen(att_name) + 1}; struct osl_row *row; int ret = osl_get_row(attribute_table, ATTCOL_NAME, &obj, &row); if (ret < 0) return ret; ret = osl_get_object(attribute_table, row, ATTCOL_BITNUM, &obj); if (ret < 0) return ret; *bitnum = *(unsigned char *)obj.data; return 1; } /** * Flags used by the lsatt command. * * \param \sa com_lsatt(). */ enum lsatt_flags { /** Whether "-a" was given for the lsatt command. */ LSATT_FLAG_SORT_BY_ID = 1, /** Whether "-l" was given for the lsatt command. */ LSATT_FLAG_LONG = 2, /** Reverse sort order. */ LSATT_FLAG_REVERSE = 4 }; /** Data passed to the action function of lsatt */ struct lsatt_action_data { /** The result buffer. */ struct para_buffer pb; /** The given flags for the lsatt command. */ unsigned flags; }; static int print_attribute(struct osl_table *table, struct osl_row *row, const char *name, void *data) { struct lsatt_action_data *laad = data; struct osl_object bitnum_obj; int ret; if (!(laad->flags & LSATT_FLAG_LONG)) return para_printf(&laad->pb, "%s\n", name); ret = osl_get_object(table, row, ATTCOL_BITNUM, &bitnum_obj); if (ret < 0) { para_printf(&laad->pb, "%s: %s\n", name, para_strerror(-ret)); return ret; } return para_printf(&laad->pb, "%u\t%s\n", *(unsigned char*)bitnum_obj.data, name); } static void com_lsatt_callback(int fd, const struct osl_object *query) { struct lsatt_action_data laad = { .flags = *(unsigned *) query->data, .pb = { .max_size = SHMMAX, .private_data = &fd, .max_size_handler = pass_buffer_as_shm } }; struct pattern_match_data pmd = { .table = attribute_table, .loop_col_num = ATTCOL_BITNUM, .match_col_num = ATTCOL_NAME, .patterns = {.data = (char *)query->data + sizeof(laad.flags), .size = query->size - sizeof(laad.flags)}, .pm_flags = PM_NO_PATTERN_MATCHES_EVERYTHING, .data = &laad, .action = print_attribute }; if (laad.flags & LSATT_FLAG_SORT_BY_ID) pmd.loop_col_num = ATTCOL_NAME; if (laad.flags & LSATT_FLAG_REVERSE) pmd.pm_flags |= PM_REVERSE_LOOP; for_each_matching_row(&pmd); if (laad.pb.offset) pass_buffer_as_shm(laad.pb.buf, laad.pb.offset, &fd); free(laad.pb.buf); } int com_lsatt(int fd, int argc, char * const * const argv) { unsigned flags = 0; struct osl_object options = {.data = &flags, .size = sizeof(flags)}; int ret, i; for (i = 1; i < argc; i++) { const char *arg = argv[i]; if (arg[0] != '-') break; if (!strcmp(arg, "--")) { i++; break; } if (!strcmp(arg, "-i")) { flags |= LSATT_FLAG_SORT_BY_ID; continue; } if (!strcmp(arg, "-l")) { flags |= LSATT_FLAG_LONG; continue; } if (!strcmp(arg, "-r")) { flags |= LSATT_FLAG_REVERSE; continue; } } ret = send_option_arg_callback_request(&options, argc - i, argv + i, com_lsatt_callback, send_result, &fd); if (!ret) { if (argc > 1) ret = send_va_buffer(fd, "no matches\n"); } else if (ret < 0) send_va_buffer(fd, "%s\n", para_strerror(-ret)); return ret; } static void com_setatt_callback(__a_unused int fd, const struct osl_object *query) { char *p; uint64_t add_mask = 0, del_mask = 0; int ret; size_t len; struct osl_object obj; struct osl_row *row; for (p = query->data; p < (char *)query->data + query->size; p += len + 1) { char c; len = strlen(p); ret = -E_ATTR_SYNTAX; if (!*p) goto out; c = p[len - 1]; if (c != '+' && c != '-') break; p[len - 1] = '\0'; obj.data = p; obj.size = len + 1; ret = osl_get_row(attribute_table, ATTCOL_NAME, &obj, &row); if (ret < 0) goto out; ret = osl_get_object(attribute_table, row, ATTCOL_BITNUM, &obj); if (ret < 0) goto out; if (c == '+') add_mask |= (1UL << *(unsigned char *)obj.data); else del_mask |= (1UL << *(unsigned char *)obj.data); } ret = -E_ATTR_SYNTAX; if (!add_mask && !del_mask) goto out; PARA_DEBUG_LOG("masks: %llx:%llx\n",(long long unsigned)add_mask, (long long unsigned)del_mask); for (; p < (char *)query->data + query->size; p += len + 1) { /* TODO: fnmatch */ struct afs_info old_afsi, new_afsi; struct afsi_change_event_data aced = {.old_afsi = &old_afsi}; len = strlen(p); ret = aft_get_row_of_path(p, &aced.aft_row); if (ret < 0) goto out; ret = get_afsi_object_of_row(aced.aft_row, &obj); if (ret < 0) goto out; ret = load_afsi(&old_afsi, &obj); if (ret < 0) goto out; new_afsi = old_afsi; new_afsi.attributes |= add_mask; new_afsi.attributes &= ~del_mask; save_afsi(&new_afsi, &obj); /* in-place update */ afs_event(AFSI_CHANGE, NULL, &aced); } out: if (ret < 0) PARA_NOTICE_LOG("%s\n", para_strerror(-ret)); } int com_setatt(__a_unused int fd, int argc, char * const * const argv) { if (argc < 3) return -E_ATTR_SYNTAX; return send_standard_callback_request(argc - 1, argv + 1, com_setatt_callback, NULL, NULL); } struct addatt_event_data { const char *name; unsigned char bitnum; }; static void com_addatt_callback(int fd, const struct osl_object *query) { char *p; int ret = 1, ret2 = 0; struct para_buffer pb = { .max_size = SHMMAX, .private_data = &fd, .max_size_handler = pass_buffer_as_shm }; size_t len; for (p = query->data; p < (char *)query->data + query->size; p += len + 1) { struct osl_object objs[NUM_ATT_COLUMNS]; struct osl_row *row; unsigned char bitnum; struct addatt_event_data aed; len = strlen(p); if (!len || p[len - 1] == '-' || p[len - 1] == '+') { ret2 = para_printf(&pb, "invalid attribute name: %s\n", p); if (ret2 < 0) goto out; continue; } ret = get_attribute_bitnum_by_name(p, &bitnum); if (ret >= 0) { ret2 = para_printf(&pb, "attribute \"%s\" already exists\n", p); if (ret2 < 0) goto out; continue; } if (ret != -E_RB_KEY_NOT_FOUND) /* error */ goto out; objs[ATTCOL_BITNUM].size = 1; /* find smallest unused attribute */ for (bitnum = 0; bitnum < 64; bitnum++) { objs[ATTCOL_BITNUM].data = &bitnum; ret = osl_get_row(attribute_table, ATTCOL_BITNUM, &objs[ATTCOL_BITNUM], &row); if (ret == -E_RB_KEY_NOT_FOUND) break; /* this bitnum is unused, use it */ if (ret < 0) /* error */ goto out; /* this bit is already in use, try next bit */ } if (bitnum == 64) { ret = -E_ATT_TABLE_FULL; goto out; } objs[ATTCOL_NAME].data = p; objs[ATTCOL_NAME].size = len + 1; ret = osl_add_row(attribute_table, objs); if (ret < 0) goto out; aed.name = p; aed.bitnum = bitnum; afs_event(ATTRIBUTE_ADD, &pb, &aed); greatest_att_bitnum = PARA_MAX(greatest_att_bitnum, (int)bitnum); } out: if (ret < 0 && ret2 >= 0) para_printf(&pb, "%s: %s\n", p, para_strerror(-ret)); if (pb.offset) pass_buffer_as_shm(pb.buf, pb.offset, &fd); free(pb.buf); } int com_addatt(int fd, int argc, char * const * const argv) { int ret; if (argc < 2) return -E_ATTR_SYNTAX; ret = send_standard_callback_request(argc - 1, argv + 1, com_addatt_callback, send_result, &fd); if (ret < 0) send_va_buffer(fd, "%s\n", para_strerror(-ret)); return ret; } static void com_mvatt_callback(int fd, const struct osl_object *query) { char *old = query->data; size_t size = strlen(old) + 1; char *new = old + size; struct osl_object obj = {.data = old, .size = size}; struct osl_row *row; struct para_buffer pb = { .max_size = SHMMAX, .private_data = &fd, .max_size_handler = pass_buffer_as_shm }; int ret; ret = osl_get_row(attribute_table, ATTCOL_NAME, &obj, &row); if (ret < 0) goto out; obj.data = new; obj.size = strlen(new) + 1; ret = osl_update_object(attribute_table, row, ATTCOL_NAME, &obj); out: if (ret < 0) para_printf(&pb, "%s\n", para_strerror(-ret)); else afs_event(ATTRIBUTE_RENAME, &pb, NULL); if (pb.offset) pass_buffer_as_shm(pb.buf, pb.offset, &fd); free(pb.buf); } int com_mvatt(int fd, int argc, char * const * const argv) { int ret; if (argc != 3) return -E_ATTR_SYNTAX; ret = send_standard_callback_request(argc - 1, argv + 1, com_mvatt_callback, send_result, &fd); if (ret < 0) send_va_buffer(fd, "%s\n", para_strerror(-ret)); return ret; } /** Data passed to the action handler of com_rmatt(). */ struct remove_attribute_action_data { /** Message buffer. */ struct para_buffer pb; /** Numver of attributes removed. */ int num_removed; /** Bitwise "or" of the removed attributes. */ uint64_t mask_of_removed_atts; }; static int remove_attribute(struct osl_table *table, struct osl_row *row, const char *name, void *data) { struct remove_attribute_action_data *raad = data; int ret; struct rmatt_event_data red = {.name = name}; ret = get_attribute_bitnum_by_name(name, &red.bitnum); if (ret < 0) return para_printf(&raad->pb, "%s: %s\n", name, para_strerror(-ret)); ret = osl_del_row(table, row); if (ret < 0) return para_printf(&raad->pb, "%s: %s\n", name, para_strerror(-ret)); ret = para_printf(&raad->pb, "removed attribute %s\n", name); raad->num_removed++; raad->mask_of_removed_atts |= (1 << red.bitnum); afs_event(ATTRIBUTE_REMOVE, &raad->pb, &red); return ret; } static void com_rmatt_callback(int fd, const struct osl_object *query) { struct remove_attribute_action_data raad = { .num_removed = 0, .pb = { .max_size = SHMMAX, .private_data = &fd, .max_size_handler = pass_buffer_as_shm } }; int ret, ret2 = 0; struct pattern_match_data pmd = { .table = attribute_table, .patterns = *query, .loop_col_num = ATTCOL_BITNUM, .match_col_num = ATTCOL_NAME, .data = &raad, .action = remove_attribute }; ret = for_each_matching_row(&pmd); if (ret < 0) ret2 = para_printf(&raad.pb, "%s\n", para_strerror(-ret)); else if (!raad.num_removed) ret2 = para_printf(&raad.pb, "no match -- nothing removed\n"); if (ret2 >= 0 && raad.pb.offset) pass_buffer_as_shm(raad.pb.buf, raad.pb.offset, &fd); free(raad.pb.buf); } int com_rmatt(int fd, int argc, char * const * const argv) { int ret; if (argc < 2) return -E_ATTR_SYNTAX; ret = send_standard_callback_request(argc - 1, argv + 1, com_rmatt_callback, send_result, &fd); if (ret < 0) send_va_buffer(fd, "%s\n", para_strerror(-ret)); return ret; } /** * Return a binary representation of the given attribute value. * * \param atts Pointer to the attribute value. * \param buf Result. * * This function prints a string of at most 64 characters plus the terminating * \p NULL character into \a buf which must be provided by the caller and at * least 65 bytes long. The "x" character is used for set attributes and "-" is * used for unset attributes. * * In practice, not all 64 attributes are defined. In this case, the function * only prints \a N + 1 charaters where \a N is the greatest id of a defined * attribute. */ void get_attribute_bitmap(const uint64_t *atts, char *buf) { int i; const uint64_t one = 1; for (i = 0; i <= greatest_att_bitnum; i++) buf[greatest_att_bitnum - i] = (*atts & (one << i))? 'x' : '-'; buf[i] = '\0'; } /** * Get a string containing the set attributes in text form. * * \param atts The attribute bitmap. * \param delim The delimiter to separate matching attribute names. * \param text Result pointer. * * \return Positive on success, negative on errors. If no attributes have * been defined, \a *text is NULL. */ int get_attribute_text(uint64_t *atts, const char *delim, char **text) { int i, ret; const uint64_t one = 1; *text = NULL; if (greatest_att_bitnum < 0) { /* no attributes available */ *text = para_strdup("(no attributes available)"); return 1; } for (i = 0; i <= greatest_att_bitnum; i++) { unsigned char bn = i; struct osl_object obj = {.data = &bn, .size = 1}; struct osl_row *row; if (!(*atts & (one << i))) continue; ret = osl_get_row(attribute_table, ATTCOL_BITNUM, &obj, &row); if (ret < 0) goto err; ret = osl_get_object(attribute_table, row, ATTCOL_NAME, &obj); if (ret < 0) goto err; if (*text) { char *tmp = make_message("%s%s%s", *text, delim, (char *)obj.data); free(*text); *text = tmp; } else *text = para_strdup(obj.data); } if (!*text) /* no attributes set */ *text = para_strdup(""); return 1; err: free(*text); return ret; } /** * Close the attribute table. * * \sa osl_close_table(). */ void attribute_close(void) { osl_close_table(attribute_table, OSL_MARK_CLEAN); attribute_table = NULL; } /** * Open the attribute table. * * \param dir The database directory. * * \return Positive on success, negative on errors. * * \sa osl_open_table(). */ static int attribute_open(const char *dir) { int ret; attribute_table_desc.dir = dir; ret = osl_open_table(&attribute_table_desc, &attribute_table); greatest_att_bitnum = -1; /* no atts available */ if (ret >= 0) { find_greatest_att_bitnum(); return ret; } attribute_table = NULL; if (ret >= 0 || is_errno(-ret, ENOENT)) return 1; return ret; } static int attribute_create(const char *dir) { attribute_table_desc.dir = dir; return osl_create_table(&attribute_table_desc); } /** * Initialize the attribute table structure. * * \param t The table structure to initialize. */ void attribute_init(struct afs_table *t) { t->open = attribute_open; t->close = attribute_close; t->create = attribute_create; }