+/** Used for removing rows from a blob table. */
+struct rmblob_data {
+ /** Message buffer. */
+ struct para_buffer pb;
+ /** Number of removed blobs. */
+ unsigned num_removed;
+};
+
+static int remove_blob(struct osl_table *table, struct osl_row *row,
+ const char *name, void *data)
+{
+ struct rmblob_data *rmbd = data;
+ int ret = osl_del_row(table, row);
+ if (ret < 0) {
+ para_printf(&rmbd->pb, "%s: %s\n", name, para_strerror(-ret));
+ return ret;
+ }
+ rmbd->num_removed++;
+ return 1; /* return success to remove other matching blobs. */
+}
+
+static int com_rmblob_callback(struct osl_table *table,
+ const struct osl_object *query,
+ struct osl_object *result)
+{
+ int ret;
+ struct rmblob_data rmbd = {.num_removed = 0};
+ struct pattern_match_data pmd = {
+ .table = table,
+ .patterns = *query,
+ .loop_col_num = BLOBCOL_NAME,
+ .match_col_num = BLOBCOL_NAME,
+ .pm_flags = PM_SKIP_EMPTY_NAME,
+ .data = &rmbd,
+ .action = remove_blob
+ };
+ result->data = NULL;
+ ret = for_each_matching_row(&pmd);
+ if (ret < 0)
+ para_printf(&rmbd.pb, "%s\n", para_strerror(-ret));
+ if (!rmbd.num_removed)
+ para_printf(&rmbd.pb, "no matches, nothing removed\n");
+ else {
+ para_printf(&rmbd.pb, "removed %d blobs\n", rmbd.num_removed);
+ afs_event(BLOB_RENAME, NULL, table);
+ }
+ result->data = rmbd.pb.buf;
+ result->size = rmbd.pb.size;
+ return 1;
+}
+
+static int com_rmblob(callback_function *f, int fd, int argc,
+ char * const * const argv)
+{
+ int ret;
+ struct osl_object result;
+
+ if (argc < 2)
+ return -E_MOOD_SYNTAX;
+ ret = send_option_arg_callback_request(NULL, argc - 1, argv + 1, f,
+ &result);
+ if (ret > 0) {
+ send_buffer(fd, (char *)result.data);
+ free(result.data);
+ }
+ return ret;