--- /dev/null
+/* Volume server probe.
+ *
+ * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include "volservice.h"
+#include "afs_xg.h"
+#include "display.h"
+
+/**
+ * kafs_open_volserver - Open a Volume Server for access.
+ * @ctx: The cell and authentication context
+ * @server: The volume server details
+ *
+ * Open a Volume Server. The server is probed to determine what it's capable
+ * of and what type it is.
+ */
+bool kafs_open_volserver(struct kafs_context *ctx, struct kafs_fileserver *server)
+{
+ if (!kafs_open_endpoint(ctx))
+ return false;
+ if (!server->vs_probed)
+ return kafs_probe_volserver(ctx, server);
+ return true;
+}
+
+/*
+ * kafs_open_volserver - Close a Volume Server.
+ * @ctx: The cell and authentication context
+ * @server: The volume server details
+ *
+ * Close a volume server, potentially releasing any resources held.
+ */
+void kafs_close_volserver(struct kafs_context *ctx,
+ struct kafs_fileserver *server)
+{
+ put_kafs_fileserver(server);
+}
+
+/**
+ * kafs_probe_volserver - Probe a Volume Server
+ * @ctx: The cell and authentication context
+ * @server: The volume server details
+ *
+ * Probe a volume server to find what it supports.
+ */
+bool kafs_probe_volserver(struct kafs_context *ctx,
+ struct kafs_fileserver *server)
+{
+ struct rxrpc_call_params *params = &server->vs_params;
+ struct sockaddr_rxrpc *srx;
+ unsigned int *caps, nr_caps;
+ unsigned int addr = 0, i;
+ char abuf[1024];
+
+ _enter("");
+
+try_next_address:
+ if (addr >= server->vs_nr_addrs) {
+ if (!ctx->result.source)
+ return kafs_error(ctx, "Volume server not responding");
+ _leave(" = f [no more addrs]");
+ return false;
+ }
+
+ srx = &server->vs_addrs[addr];
+ memset(params, 0, sizeof(*params));
+ params->endpoint = ctx->endpoint;
+ params->security = ctx->security;
+ params->peer = *srx;
+ params->peer_len = sizeof(*srx);
+ params->upgrade_service = true;
+
+ kafs_sprint_address(ctx, ¶ms->peer, abuf, sizeof(abuf));
+ _debug("addr %u %s", addr, abuf);
+
+ if (!VOLSER_GetCapabilities(params, &caps, &nr_caps, &ctx->result)) {
+ _debug("failed %u %d", ctx->result.source, ctx->result.abort_code);
+ switch (ctx->result.source) {
+ case rxrpc_error_remote_abort:
+ switch (ctx->result.abort_code) {
+ case RX_INVALID_OPERATION:
+ case RXGEN_OPCODE:
+ server->vs_service = kafs_service_afs;
+ params->upgrade_service = false;
+ memset(&server->vs_caps, 0, sizeof(server->vs_caps));
+ ctx->result.source = rxrpc_error_none;
+ goto out;
+ default:
+ return false;
+ }
+
+ case rxrpc_error_from_network:
+ case rxrpc_error_from_system:
+ switch (ctx->result.error) {
+ case -ENONET:
+ case -ECONNRESET: /* Responded, but call expired. */
+ case -ERFKILL:
+ case -EADDRNOTAVAIL:
+ case -ENETUNREACH:
+ case -EHOSTUNREACH:
+ case -EHOSTDOWN:
+ case -ECONNREFUSED:
+ case -ETIMEDOUT:
+ case -ETIME:
+ addr++;
+ goto try_next_address;
+ default:
+ return false;
+ }
+
+ default:
+ return false;
+ }
+ }
+
+ params->upgrade_service = false;
+ server->vs_pref = addr;
+ switch (ctx->result.service_id) {
+ case VOLSERVICE_ID:
+ server->vs_service = kafs_service_afs;
+ break;
+ case YFS_VOL_SERVICE:
+ server->vs_service = kafs_service_yfs;
+ params->peer.srx_service = ctx->result.service_id;
+ for (i = 0; i < server->vs_nr_addrs; i++)
+ server->vs_addrs[i].srx_service = ctx->result.service_id;
+ break;
+ default:
+ free(caps);
+ return kafs_error(ctx, "Unsupported VL service %u", ctx->result.service_id);
+ }
+
+ memset(&server->vs_caps, 0, sizeof(server->vs_caps));
+ nr_caps *= sizeof(unsigned int);
+ if (nr_caps > sizeof(server->vs_caps))
+ nr_caps = sizeof(server->vs_caps);
+ memcpy(&server->vs_caps, caps, nr_caps);
+ free(caps);
+
+out:
+ if (kafs_debug_caps) {
+ printf("volser-caps:");
+ for (i = 0; i < sizeof(server->vs_caps) / sizeof(unsigned int); i++)
+ printf(" %08x", server->vs_caps[i]);
+ if (server->vs_service == kafs_service_yfs)
+ printf(" yfs\n");
+ else
+ printf(" afs\n");
+ }
+
+ _leave(" = t");
+ return true;
+}
--- /dev/null
+/* Volume information query
+ *
+ * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "volservice.h"
+#include "afs_xg.h"
+
+/**
+ * kafs_vol_monitor_transaction - Get the state of ongoing transactions on a volume server
+ * @ctx: The cell and authentication context
+ * @server: The volume server to contact
+ * @info_list: The list to which the records will be added
+ *
+ * Retrieve the list of ongoing transactions from a volume server and details
+ * of those transactions. The transaction records are added to the list.
+ */
+bool kafs_vol_monitor_transaction(struct kafs_context *ctx,
+ struct kafs_fileserver *server,
+ struct kafs_vol_transaction_info_list *info_list)
+{
+ struct kafs_vol_transaction_info *list = NULL;
+ struct transDebugInfo **results;
+ unsigned int count, i;
+
+ if (!VOLSER_Monitor(&server->vs_params, &results, &count, &ctx->result))
+ return false;
+
+ if (!count)
+ goto done;
+
+ list = calloc(count, sizeof(list[0]));
+ if (!list)
+ return kafs_nomem(ctx);
+
+ for (i = 0; i < count; i++) {
+ struct kafs_vol_transaction_info *info = &list[i];
+ struct transDebugInfo *r = results[i];
+
+ info->tid = r->tid;
+ info->time = r->time;
+ info->creation_time = r->creationTime;
+ info->return_code = r->returnCode;
+ info->volid = r->volid;
+ info->partition = r->partition;
+ info->iflags = r->iflags;
+ info->vflags = r->vflags;
+ info->tflags = r->tflags;
+ info->call_valid = r->callValid;
+ info->read_next = r->readNext;
+ info->transmit_next = r->transmitNext;
+ info->last_send_time = r->lastSendTime;
+ info->last_receive_time = r->lastReceiveTime;
+
+ strncpy(info->last_proc_name, r->lastProcName, sizeof(info->last_proc_name));
+ }
+
+done:
+ rxgen_bulk_free_transDebugInfo(results, count);
+ info_list->count = count;
+ info_list->info = list;
+ return true;
+}
+
+/*
+ * Convert a volintInfo struct to a kafs_volume_info record.
+ */
+static void kafs_volintinfo_to_volume_info(struct volintInfo *info,
+ struct kafs_volume_info *vi)
+{
+ strcpy(vi->name, info->name);
+ vi->volid = info->volid;
+ vi->type = info->type;
+ vi->backup_id = info->backupID;
+ vi->parent_id = info->parentID;
+ vi->clone_id = info->cloneID;
+ vi->status = info->status;
+ vi->copy_date = info->copyDate;
+ vi->in_use = info->inUse;
+ vi->needs_salvage = info->needsSalvaged;
+ vi->destroy_me = info->destroyMe;
+ vi->creation_date = info->creationDate;
+ vi->access_date = info->accessDate;
+ vi->update_date = info->updateDate;
+ vi->backup_date = info->backupDate;
+ vi->day_use = info->dayUse;
+ vi->file_count = info->filecount;
+ vi->max_quota = info->maxquota;
+ vi->size = info->size;
+ vi->flags = info->flags;
+ vi->min_quota = info->spare0;
+ vi->week_use = info->spare1;
+ vi->spare2 = info->spare2;
+ vi->spare3 = info->spare3;
+
+ vi->has_flags = true;
+ vi->has_need_salvage = true;
+ vi->has_min_quota = true;
+ vi->has_week_use = true;
+ vi->has_spare2 = true;
+ vi->has_spare3 = true;
+}
+
+/*
+ * Convert an xvolintInfo struct to a kafs_volume_info record.
+ */
+static void kafs_xvolintinfo_to_volume_info(struct xvolintInfo *xinfo,
+ struct kafs_volume_info *vi)
+{
+ int i;
+
+ strcpy(vi->name, xinfo->name);
+ vi->volid = xinfo->volid;
+ vi->type = xinfo->type;
+ vi->backup_id = xinfo->backupID;
+ vi->parent_id = xinfo->parentID;
+ vi->clone_id = xinfo->cloneID;
+ vi->status = xinfo->status;
+ vi->copy_date = xinfo->copyDate;
+ vi->in_use = xinfo->inUse;
+ vi->creation_date = xinfo->creationDate;
+ vi->access_date = xinfo->accessDate;
+ vi->update_date = xinfo->updateDate;
+ vi->backup_date = xinfo->backupDate;
+ vi->day_use = xinfo->dayUse;
+ vi->file_count = xinfo->filecount;
+ vi->max_quota = xinfo->maxquota;
+ vi->size = xinfo->size;
+
+ for (i = 0; i < 4; i++) {
+ vi->stat_reads[i] = xinfo->stat_reads[i];
+ vi->stat_writes[i] = xinfo->stat_writes[i];
+ }
+
+ for (i = 0; i < 6; i++) {
+ vi->stat_fileSameAuthor[i] = vi->stat_fileSameAuthor[i];
+ vi->stat_fileDiffAuthor[i] = vi->stat_fileDiffAuthor[i];
+ vi->stat_dirSameAuthor[i] = vi->stat_dirSameAuthor[i];
+ vi->stat_dirDiffAuthor[i] = vi->stat_dirDiffAuthor[i];
+ }
+
+ vi->has_stats = true;
+}
+
+/**
+ * kafs_VOLSER_ListOneVolume - Get the ordinary state of an instance of a volume
+ * @ctx: The cell and authentication context
+ * @server: The volume server to contact
+ * @partition: The partition on which the volume resides
+ * @volid: The ID of the volume to query
+ * @vi: The record to load with the information retrieved
+ *
+ * Query the ordinary state of a volume on a volume server. The information is
+ * stored into the supplied record.
+ */
+bool kafs_VOLSER_ListOneVolume(struct kafs_context *ctx,
+ struct kafs_fileserver *server,
+ struct kafs_partition *partition,
+ kafs_volume_id_t volid,
+ struct kafs_volume_info *vi)
+{
+ struct volintInfo **infos;
+ unsigned int nr;
+ bool ret;
+
+ ret = VOLSER_ListOneVolume(&server->vs_params, partition->id, volid,
+ &infos, &nr, &ctx->result);
+ if (ret) {
+ if (nr)
+ kafs_volintinfo_to_volume_info(infos[0], vi);
+ else
+ ret = kafs_error(ctx, "No volume information results returned");
+ rxgen_bulk_free_volintInfo(infos, nr);
+ }
+
+ return ret;
+}
+
+/**
+ * kafs_VOLSER_XListOneVolume - Get the extended state of an instance of a volume
+ * @ctx: The cell and authentication context
+ * @server: The volume server to contact
+ * @partition: The partition on which the volume resides
+ * @volid: The ID of the volume to query
+ * @vi: The record to load with the information retrieved
+ *
+ * Query the extended state of a volume on a volume server. The information is
+ * stored into the supplied record.
+ */
+bool kafs_VOLSER_XListOneVolume(struct kafs_context *ctx,
+ struct kafs_fileserver *server,
+ struct kafs_partition *partition,
+ kafs_volume_id_t volid,
+ struct kafs_volume_info *vi)
+{
+ struct xvolintInfo **xinfos;
+ unsigned int nr;
+ bool ret;
+
+ ret = VOLSER_XListOneVolume(&server->vs_params, partition->id, volid,
+ &xinfos, &nr, &ctx->result);
+ if (ret) {
+ if (nr)
+ kafs_xvolintinfo_to_volume_info(xinfos[0], vi);
+ else
+ ret = kafs_error(ctx, "No volume information results returned");
+ rxgen_bulk_free_xvolintInfo(xinfos, nr);
+ }
+
+ return ret;
+}
+
+/**
+ * kafs_query_volume_state - Query the state of an instance of a volume
+ * @ctx: The cell and authentication context
+ * @server: The volume server to contact
+ * @partition: The partition on which the volume resides
+ * @volid: The ID of the volume to query
+ * @extended: True if the extended state is desired; false otherwise.
+ * @_vi: Where to place the result record.
+ *
+ * Query the ordinary or extended state of a volume on a volume server and
+ * transcribe to a record which is placed at *@_vi.
+ */
+bool kafs_query_volume_state(struct kafs_context *ctx,
+ struct kafs_fileserver *server,
+ struct kafs_partition *partition,
+ kafs_volume_id_t volid,
+ bool extended,
+ struct kafs_volume_info **_vi)
+{
+ struct kafs_volume_info *vi;
+ bool ret;
+
+ vi = calloc(1, sizeof(*vi));
+ if (!vi)
+ return kafs_nomem(ctx);
+
+ if (extended)
+ ret = kafs_VOLSER_XListOneVolume(ctx, server, partition, volid, vi);
+ else
+ ret = kafs_VOLSER_ListOneVolume(ctx, server, partition, volid, vi);
+ if (!ret) {
+ free(vi);
+ return false;
+ }
+
+ *_vi = vi;
+ return true;
+}
+
+/**
+ * kafs_list_partitions - Get a list of partitions on a server.
+ * @ctx: The cell and authentication context
+ * @server: The volume server to contact
+ * @partition_list: Where to store the list of partitions.
+ *
+ * Query a volume server and retrieve the list of volumes attached to it.
+ */
+bool kafs_list_partitions(struct kafs_context *ctx,
+ struct kafs_fileserver *server,
+ struct kafs_partition_list *partition_list)
+{
+ struct kafs_partition *parts = NULL;
+ unsigned int count;
+ int32_t *ents;
+ int i;
+
+ if (!VOLSER_XListPartitions(&server->vs_params, &ents, &count, &ctx->result))
+ return false;
+
+ if (count) {
+ parts = calloc(count, sizeof(*parts));
+ if (!parts) {
+ free(ents);
+ return true;
+ }
+
+ for (i = 0; i < count; i++)
+ parts[i].id = ents[i];
+ }
+
+ partition_list->count = count;
+ partition_list->parts = parts;
+ free(ents);
+ return true;
+}
--- /dev/null
+/* KAFS Volume service mid-level client library.
+ *
+ * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#ifndef VOLSERVICE_H
+#define VOLSERVICE_H
+
+#include "rxrpc.h"
+#include "kafs.h"
+#include "afs_xg.h"
+
+/*
+ * Volume transaction information.
+ *
+ * Source transDebugInfo.
+ */
+struct kafs_vol_transaction_info {
+ uint32_t tid;
+ time_t time;
+ time_t creation_time;
+ uint32_t return_code;
+ uint32_t volid;
+ uint32_t partition;
+ uint16_t iflags;
+ uint8_t vflags;
+ uint8_t tflags;
+ char last_proc_name[30];
+ uint32_t call_valid;
+ uint32_t read_next;
+ uint32_t transmit_next;
+ time_t last_send_time;
+ time_t last_receive_time;
+};
+
+struct kafs_vol_transaction_info_list {
+ unsigned int count;
+ struct kafs_vol_transaction_info *info;
+};
+
+/*
+ * Volume information.
+ *
+ * Source: volintInfo, xvolintInfo.
+ */
+struct kafs_volume_info {
+ kafs_volume_id_t volid;
+ kafs_volume_id_t backup_id;
+ kafs_volume_id_t parent_id;
+ kafs_volume_id_t clone_id;
+ unsigned int type;
+ unsigned int status;
+ unsigned int copy_date;
+ bool in_use;
+ bool needs_salvage;
+ bool destroy_me;
+ time_t creation_date;
+ time_t access_date;
+ time_t update_date;
+ time_t backup_date;
+ unsigned int day_use;
+ unsigned int week_use; /* spare1 */
+ unsigned int file_count;
+ unsigned int max_quota;
+ unsigned int min_quota; /* spare0 */
+ unsigned int size;
+ unsigned int flags;
+
+ int stat_reads[4];
+ int stat_writes[4];
+ int stat_fileSameAuthor[6];
+ int stat_fileDiffAuthor[6];
+ int stat_dirSameAuthor[6];
+ int stat_dirDiffAuthor[6];
+
+ unsigned int spare2;
+ unsigned int spare3;
+
+ bool has_flags:1;
+ bool has_need_salvage:1;
+ bool has_min_quota:1;
+ bool has_stats:1;
+ bool has_week_use:1;
+ bool has_spare2:1;
+ bool has_spare3:1;
+
+ char name[VNAMESIZE];
+};
+
+/*
+ * List of partitions.
+ */
+struct kafs_partition_list {
+ unsigned int count;
+ struct kafs_partition *parts;
+};
+
+/*
+ * vol_probe.c
+ */
+extern bool kafs_open_volserver(struct kafs_context *, struct kafs_fileserver *);
+extern void kafs_close_volserver(struct kafs_context *, struct kafs_fileserver *);
+extern bool kafs_probe_volserver(struct kafs_context *, struct kafs_fileserver *);
+
+/*
+ * vol_query.c
+ */
+extern bool kafs_vol_monitor_transaction(struct kafs_context *, struct kafs_fileserver *,
+ struct kafs_vol_transaction_info_list *);
+extern bool kafs_list_partitions(struct kafs_context *, struct kafs_fileserver *,
+ struct kafs_partition_list *);
+extern bool kafs_query_volume_state(struct kafs_context *, struct kafs_fileserver *,
+ struct kafs_partition *, kafs_volume_id_t, bool,
+ struct kafs_volume_info **);
+
+/*
+ * Inline functions
+ */
+static inline
+void clear_kafs_partition_list(struct kafs_partition_list *partitions)
+{
+ free(partitions->parts);
+}
+
+static inline
+void clear_kafs_vol_transaction_info_list(struct kafs_vol_transaction_info_list *info_list)
+{
+ free(info_list->info);
+ info_list->info = NULL;
+}
+
+#endif /* VLSERVICE_H */