Add a couple of layers for VLDB querying and management.
The lower layer wraps collections of similar VL querying RPC calls, that
work out which variant to call and extract the retrieved data out into a
common data type. Such functions include:
kafs_VL_GetAddrs_by_uuid()
kafs_VL_GetAddrs_by_addr()
kafs_VL_GetAddrs_by_index()
kafs_VL_GetEntryByName()
kafs_VL_ListAttributes()
On top of that is a middle layer that may call sequences of the above and
add caching of the results:
kafs_open_vl_service()
kafs_probe_vl_service()
kafs_look_up_volume_by_name()
kafs_look_up_volumes_by_attributes()
kafs_look_up_fileserver_by_uuid()
kafs_look_up_fileserver_by_addr()
kafs_look_up_fileserver_by_index()
kafs_map_volume_locations()
Signed-off-by: David Howells <dhowells@redhat.com>
kafs.c \
arg_completion.c \
arg_parse.c \
- display_error.c
+ display_error.c \
+ vl_fileservers.c \
+ vl_probe.c \
+ vl_volumes.c
BOS_SRCS := \
bos.c \
#define FS_PORT 7000 /* AFS file server port */
#define FS_SERVICE 1 /* AFS File Service ID */
+#define VL_PORT 7003 /* Volume location service port */
+#define VL_SERVICE 52 /* RxRPC service ID for the Volume Location service */
+#define YFS_VL_SERVICE 2503 /* Service ID for AuriStor upgraded VL service */
typedef unsigned long long kafs_volume_id_t;
+struct afsUUID;
enum kafs_service_t {
kafs_service_afs,
kafs_service_yfs,
};
+struct kafs_fileserver {
+ uuid_t uuid;
+ bool inited:1; /* T if initialised */
+ bool has_uuid:1;
+ unsigned int ref;
+ unsigned int fs_nr_addrs;
+ unsigned int vs_nr_addrs;
+ struct sockaddr_rxrpc *fs_addrs; /* Fileserver addresses */
+ struct sockaddr_rxrpc *vs_addrs; /* Volume server addresses */
+ char *name; /* Canonical server name */
+};
+
struct kafs_context {
struct rxrpc_local_endpoint *endpoint;
struct rxrpc_security *security;
struct rxrpc_result result;
+ bool no_resolve; /* Don't resolve addresses for display */
char *cell_name;
struct kafs_cell *cell_db;
unsigned int vl_caps[1]; /* VL server capabilities */
struct rxrpc_call_params vl_params; /* Parameters for accessing pref VL server */
+ unsigned int fs_nr_servers; /* Number of fileserver records */
+ struct kafs_fileserver **fs_servers; /* Array of fileserver records */
+
enum rxrpc_security_auth_level sec_level; /* Security level */
char err_buf[1024];
};
extern struct kafs_lookup_context kafs_lookup_context;
extern bool kafs_open_endpoint(struct kafs_context *);
+/*
+ * vl_fileservers.c
+ */
+extern void put_kafs_fileserver(struct kafs_fileserver *fs);
+
/*
* Inline functions.
*/
--- /dev/null
+/* Fileserver/volumeserver record lookup and caching.
+ *
+ * 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 "vlservice.h"
+#include "afs_xg.h"
+
+void put_kafs_fileserver(struct kafs_fileserver *fs)
+{
+ if (fs && --fs->ref == 0) {
+ free(fs->fs_addrs);
+ free(fs->vs_addrs);
+ free(fs);
+ }
+}
+
+/*
+ * Allocate a fileserver record.
+ */
+static struct kafs_fileserver *kafs_alloc_fileserver(struct kafs_context *ctx)
+{
+ struct kafs_fileserver *server;
+
+ server = calloc(1, sizeof(*server));
+ if (!server) {
+ kafs_nomem(ctx);
+ return NULL;
+ }
+
+ server->ref = 1;
+ return server;
+}
+
+static bool kafs_cmp_fs_uuid(const void *_a, const struct kafs_fileserver *b)
+{
+ const uuid_t *a = _a;
+ return memcmp(a, &b->uuid, sizeof(*a)) == 0;
+}
+
+static bool kafs_cmp_fs_addr(const void *_a, const struct kafs_fileserver *b)
+{
+ const struct sockaddr_rxrpc *a = _a, *p;
+ int i;
+
+ for (i = 0; i < b->fs_nr_addrs; i++) {
+ p = &b->fs_addrs[i];
+ if (p->transport.family != a->transport.family)
+ continue;
+ switch (a->transport.family) {
+ case AF_INET:
+ if (p->transport.sin.sin_port == a->transport.sin.sin_port &&
+ p->transport.sin.sin_addr.s_addr == a->transport.sin.sin_addr.s_addr)
+ return true;
+ continue;
+ case AF_INET6:
+ if (p->transport.sin6.sin6_port != a->transport.sin6.sin6_port)
+ continue;
+ if (memcmp(&p->transport.sin6.sin6_addr,
+ &a->transport.sin6.sin6_addr,
+ 16) == 0)
+ return true;
+ continue;
+ default:
+ continue;
+ }
+ }
+ return false;
+}
+
+static bool kafs_cmp_fs(const void *_a, const struct kafs_fileserver *b)
+{
+ const struct kafs_fileserver *a = _a;
+ int i;
+
+ if (a->has_uuid && b->has_uuid && kafs_cmp_fs_uuid(&a->uuid, b))
+ return true;
+
+ for (i = 0; i < a->fs_nr_addrs; i++)
+ if (kafs_cmp_fs_addr(&a->fs_addrs[i], b))
+ return true;
+ return false;
+}
+
+/*
+ * Look up a cached fileserver record by uuid. If one is not found, the
+ * candidate fileserver will be added into the cache. If the candidate would
+ * be a duplicate, it is discarded.
+ */
+static struct kafs_fileserver *kafs_cache_fileserver(
+ struct kafs_context *ctx,
+ const void *a,
+ struct kafs_fileserver *candidate,
+ bool (*test)(const void *a, const struct kafs_fileserver *b))
+{
+ struct kafs_fileserver **list = ctx->fs_servers;
+ int i = 0;
+
+ for (i = 0; i < ctx->fs_nr_servers; i++) {
+ struct kafs_fileserver *server = list[i];
+
+ if (test(a, server)) {
+ server->ref++;
+ put_kafs_fileserver(candidate);
+ return server;
+ }
+ }
+
+ if (!candidate)
+ return NULL;
+
+ list = reallocarray(list, ctx->fs_nr_servers + 1, sizeof(*list));
+ if (!list) {
+ put_kafs_fileserver(candidate);
+ return kafs_nomem(ctx), NULL;
+ }
+
+ list[ctx->fs_nr_servers] = candidate;
+ ctx->fs_servers = list;
+ ctx->fs_nr_servers++;
+ return candidate;
+}
+
+/*
+ * Convert an afsUUID object into a uuid_t.
+ */
+void afsUUID_to_uuid(uuid_t *_uuid, const struct afsUUID *au)
+{
+ struct afsUUID *uuid = (struct afsUUID *)_uuid;
+ int j;
+
+ uuid->time_low = htonl(au->time_low);
+ uuid->time_mid = htons(au->time_mid);
+ uuid->time_hi_and_version = htons(au->time_hi_and_version);
+ uuid->clock_seq_hi_and_reserved = au->clock_seq_hi_and_reserved;
+ uuid->clock_seq_low = au->clock_seq_low;
+ for (j = 0; j < 6; j++)
+ uuid->node[j] = au->node[j];
+}
+
+/*
+ * Convert a uuid_t into an afsUUID object.
+ */
+void uuid_to_afsUUID(struct afsUUID *au, const uuid_t *_uuid)
+{
+ const struct afsUUID *uuid = (const struct afsUUID *)_uuid;
+ int j;
+
+ au->time_low = ntohl(uuid->time_low);
+ au->time_mid = ntohs(uuid->time_mid);
+ au->time_hi_and_version = ntohs(uuid->time_hi_and_version);
+ au->clock_seq_hi_and_reserved = uuid->clock_seq_hi_and_reserved;
+ au->clock_seq_low = uuid->clock_seq_low;
+ for (j = 0; j < 6; j++)
+ au->node[j] = uuid->node[j];
+}
+
+/*
+ * Convert a list of IPv4 addresses into an address list.
+ */
+static struct sockaddr_rxrpc *kafs_addrs_to_addrlist(struct kafs_context *ctx,
+ uint32_t *addrlist,
+ size_t nr_addrs,
+ unsigned short port,
+ unsigned short service)
+{
+ struct sockaddr_rxrpc *alist;
+ size_t i;
+
+ alist = calloc(nr_addrs, sizeof(*alist));
+ if (!alist)
+ return kafs_nomem(ctx), NULL;
+
+ for (i = 0; i < nr_addrs; i++) {
+ alist[i].srx_family = AF_RXRPC;
+ alist[i].srx_service = service;
+ alist[i].transport_type = SOCK_DGRAM;
+ alist[i].transport_len = sizeof(alist[i].transport.sin);
+ alist[i].transport.sin.sin_family = AF_INET;
+ alist[i].transport.sin.sin_port = htons(port);
+ alist[i].transport.sin.sin_addr.s_addr = htonl(addrlist[i]);
+ }
+
+ return alist;
+}
+
+/*
+ * Convert a list of endpoints into an address list.
+ */
+static struct sockaddr_rxrpc *kafs_endpoints_to_addrlist(struct kafs_context *ctx,
+ struct endpoint **eplist,
+ size_t nr_eplist,
+ unsigned short service)
+{
+ struct sockaddr_rxrpc *alist;
+ unsigned int x[5];
+ size_t i;
+
+ alist = calloc(nr_eplist, sizeof(*alist));
+ if (!alist)
+ return kafs_nomem(ctx), NULL;
+
+ for (i = 0; i < nr_eplist; i++) {
+ struct endpoint *ep = eplist[i];
+
+ alist[i].srx_family = AF_RXRPC;
+ alist[i].srx_service = service;
+ alist[i].transport_type = SOCK_DGRAM;
+
+ switch (ep->Type) {
+ case ENDPOINT_IPV4:
+ if (ep->Data__len != 8) {
+ free(alist);
+ return kafs_error(ctx, "Malformed IPv4 Endpoint"), NULL;
+ }
+ memcpy(x, ep->Data, 8);
+ alist[i].transport_len = sizeof(alist[i].transport.sin);
+ alist[i].transport.sin.sin_family = AF_INET;
+ alist[i].transport.sin.sin_port = htons(ntohl(x[1]));
+ memcpy(&alist[i].transport.sin.sin_addr, x, 4);
+ break;
+ case ENDPOINT_IPV6:
+ if (ep->Data__len != 20) {
+ free(alist);
+ return kafs_error(ctx, "Malformed IPv4 Endpoint"), NULL;
+ }
+ memcpy(x, ep->Data, 20);
+ alist[i].transport_len = sizeof(alist[i].transport.sin6);
+ alist[i].transport.sin6.sin6_family = AF_INET6;
+ alist[i].transport.sin6.sin6_port = htons(ntohl(x[4]));
+ memcpy(&alist[i].transport.sin6.sin6_addr, x, 16);
+ break;
+ default:
+ free(alist);
+ return kafs_error(ctx, "Unsupported Endpoint type (%u)", ep->Type), NULL;
+ }
+ }
+
+ return alist;
+}
+
+/**
+ * kafs_VL_GetAddrs_by_addr - Query the VLDB for a server by UUID
+ * @ctx: The cell and authentication context
+ * @uuid: The UUID of the file/volume server
+ *
+ * Query the Volume Location service for a fileserver record by UUID. The server record may
+ * be retrieved from the cache, and if not, the result will be cached.
+ *
+ */
+struct kafs_fileserver *kafs_VL_GetAddrs_by_uuid(struct kafs_context *ctx,
+ const uuid_t *uuid)
+{
+ struct kafs_fileserver *server;
+ struct ListAddrByAttributes laba;
+ union yfsServerAttributes filter;
+ struct endpoint **fsEndpoints = NULL, **volEndpoints = NULL;
+ struct afsUUID a_uuid;
+ uint32_t *blkaddrs = NULL;
+ int32_t uniquifier, nentries;
+ bool ok = false;
+
+ server = kafs_alloc_fileserver(ctx);
+ if (!server)
+ return NULL;
+
+ /* Start off by trying the YFS VL.GetEndpoints RPC */
+ if (ctx->vl_service != kafs_service_yfs)
+ goto fallback_getaddrsu;
+
+ memset(&filter, 0, sizeof(filter));
+ filter.type = YFS_SERVER_UUID;
+ memcpy(&filter.uuid, uuid, sizeof(filter.uuid));
+
+ if (!YFSVL_GetEndpoints(&ctx->vl_params, &filter,
+ &server->uuid, &uniquifier,
+ &fsEndpoints, &server->fs_nr_addrs,
+ &volEndpoints, &server->vs_nr_addrs,
+ &ctx->result)) {
+ put_kafs_fileserver(server);
+ return rxrpc_call_failed(&ctx->result), NULL;
+ }
+
+ server->fs_addrs = kafs_endpoints_to_addrlist(ctx, fsEndpoints, server->fs_nr_addrs,
+ FS_SERVICE);
+ if (!server->fs_addrs)
+ goto out;
+ server->vs_addrs = kafs_endpoints_to_addrlist(ctx, volEndpoints, server->vs_nr_addrs,
+ VOLSERVICE_ID);
+ if (!server->vs_addrs)
+ goto out;
+
+ server->inited = true;
+ ok = true;
+ goto out;
+
+fallback_getaddrsu:
+ /* Fall back to the AFS VL.GetAddrsU RPC */
+ memset(&laba, 0, sizeof(laba));
+ laba.Mask = VLADDR_UUID;
+ uuid_to_afsUUID(&laba.uuid, uuid);
+
+ if (!VL_GetAddrsU(&ctx->vl_params, &laba,
+ &a_uuid, &uniquifier, &nentries,
+ &blkaddrs, &server->fs_nr_addrs, &ctx->result))
+ return rxrpc_call_failed(&ctx->result), NULL;
+
+ afsUUID_to_uuid(&server->uuid, &a_uuid);
+
+ server->fs_addrs = kafs_addrs_to_addrlist(ctx, blkaddrs, server->fs_nr_addrs,
+ FS_PORT, FS_SERVICE);
+ if (!server->fs_addrs)
+ goto out;
+ server->vs_addrs = kafs_addrs_to_addrlist(ctx, blkaddrs, server->vs_nr_addrs,
+ VOLSERVICE_PORT, VOLSERVICE_ID);
+ if (!server->vs_addrs)
+ goto out;
+
+ server->inited = true;
+ ok = true;
+
+out:
+ rxgen_bulk_free_endpoint(fsEndpoints, server->fs_nr_addrs);
+ rxgen_bulk_free_endpoint(volEndpoints, server->vs_nr_addrs);
+ free(blkaddrs);
+ if (!ok)
+ put_kafs_fileserver(server);
+ return ok ? server : NULL;
+}
+
+/**
+ * kafs_VL_GetAddrs_by_addr - Query the VLDB for a server by address
+ * @ctx: The cell and authentication context
+ * @addr: One of the server's endpoint addresses.
+ *
+ * Query the Volume Location service for a fileserver record by the address of one of the
+ * fileserver endpoints.
+ */
+struct kafs_fileserver *kafs_VL_GetAddrs_by_addr(struct kafs_context *ctx,
+ const struct sockaddr_rxrpc *srx)
+{
+ struct kafs_fileserver *server;
+ struct ListAddrByAttributes laba;
+ union yfsServerAttributes filter;
+ struct endpoint **fsEndpoints = NULL, **volEndpoints = NULL;
+ struct afsUUID a_uuid;
+ unsigned int port;
+ uint32_t *blkaddrs = NULL;
+ uint32_t endpoint[5];
+ int32_t uniquifier, nentries;
+ bool ok = false;
+
+ server = kafs_alloc_fileserver(ctx);
+ if (!server)
+ goto out;
+
+ /* Start off by trying the YFS VL.GetEndpoints RPC */
+ if (ctx->vl_service != kafs_service_yfs)
+ goto fallback_getaddrsu;
+
+ memset(&filter, 0, sizeof(filter));
+ filter.type = YFS_SERVER_ENDPOINT;
+
+ switch (srx->transport.family) {
+ case AF_INET:
+ memcpy(endpoint, &srx->transport.sin.sin_addr, 4);
+ port = ntohs(srx->transport.sin.sin_port);
+ if (port == 0)
+ port = FS_PORT;
+ endpoint[1] = htonl(port);
+ filter.endpoint.Type = ENDPOINT_IPV4;
+ filter.endpoint.Data = endpoint;
+ filter.endpoint.Data__len = 4 + 4;
+ break;
+ case AF_INET6:
+ memcpy(endpoint, &srx->transport.sin6.sin6_addr, 16);
+ port = ntohs(srx->transport.sin6.sin6_port);
+ if (port == 0)
+ port = FS_PORT;
+ endpoint[4] = htonl(port);
+ filter.endpoint.Type = ENDPOINT_IPV6;
+ filter.endpoint.Data = endpoint;
+ filter.endpoint.Data__len = 16 + 4;
+ break;
+ default:
+ kafs_error(ctx, "Address family %u not supported\n",
+ srx->transport.family);
+ goto out;
+ }
+
+ if (!YFSVL_GetEndpoints(&ctx->vl_params, &filter,
+ &server->uuid, &uniquifier,
+ &fsEndpoints, &server->fs_nr_addrs,
+ &volEndpoints, &server->vs_nr_addrs,
+ &ctx->result)) {
+ put_kafs_fileserver(server);
+ return rxrpc_call_failed(&ctx->result), NULL;
+ }
+
+ server->fs_addrs = kafs_endpoints_to_addrlist(ctx, fsEndpoints, server->fs_nr_addrs,
+ FS_SERVICE);
+ if (!server->fs_addrs)
+ goto out;
+ server->vs_addrs = kafs_endpoints_to_addrlist(ctx, volEndpoints, server->vs_nr_addrs,
+ VOLSERVICE_PORT);
+ if (!server->vs_addrs)
+ goto out;
+
+ server->inited = true;
+ ok = true;
+ goto out;
+
+fallback_getaddrsu:
+ /* Fall back to the AFS VL.GetAddrsU RPC */
+ memset(&laba, 0, sizeof(laba));
+ switch (srx->transport.family) {
+ case AF_INET:
+ laba.Mask = VLADDR_IPADDR;
+ memcpy(&laba.ipaddr, &srx->transport.sin.sin_addr, 4);
+ break;
+ default:
+ kafs_error(ctx, "Address family %u not supported\n",
+ srx->transport.family);
+ goto out;
+ }
+
+ if (!VL_GetAddrsU(&ctx->vl_params, &laba,
+ &a_uuid, &uniquifier, &nentries,
+ &blkaddrs, &server->fs_nr_addrs, &ctx->result))
+ return rxrpc_call_failed(&ctx->result), NULL;
+
+ afsUUID_to_uuid(&server->uuid, &a_uuid);
+
+ server->fs_addrs = kafs_addrs_to_addrlist(ctx, blkaddrs, server->fs_nr_addrs,
+ FS_PORT, FS_SERVICE);
+ if (!server->fs_addrs)
+ goto out;
+ server->vs_addrs = kafs_addrs_to_addrlist(ctx, blkaddrs, server->vs_nr_addrs,
+ VOLSERVICE_PORT, VOLSERVICE_ID);
+ if (!server->vs_addrs)
+ goto out;
+
+ server->inited = true;
+ ok = true;
+
+out:
+ rxgen_bulk_free_endpoint(fsEndpoints, server->fs_nr_addrs);
+ rxgen_bulk_free_endpoint(volEndpoints, server->vs_nr_addrs);
+ free(blkaddrs);
+ if (!ok)
+ put_kafs_fileserver(server);
+ return ok ? server : NULL;
+}
+
+/**
+ * kafs_VL_GetAddrs_by_index - Query the VLDB for a server by index
+ * @ctx: The cell and authentication context
+ * @server_index: The index number of the server within the cell
+ *
+ * Query the Volume Location service for a fileserver record by index.
+ */
+struct kafs_fileserver *kafs_VL_GetAddrs_by_index(struct kafs_context *ctx,
+ int server_index)
+{
+ struct kafs_fileserver *server;
+ struct ListAddrByAttributes laba;
+ union yfsServerAttributes filter;
+ struct endpoint **fsEndpoints = NULL, **volEndpoints = NULL;
+ struct afsUUID a_uuid;
+ uint32_t *blkaddrs = NULL;
+ int32_t uniquifier, nentries;
+ bool ok = false;
+
+ server = kafs_alloc_fileserver(ctx);
+ if (!server)
+ goto out;
+
+ /* Start off by trying the YFS VL.GetEndpoints RPC */
+ if (ctx->vl_service != kafs_service_yfs)
+ goto fallback_getaddrsu;
+
+ memset(&filter, 0, sizeof(filter));
+ filter.type = YFS_SERVER_INDEX;
+ filter.index = server_index;
+
+ if (!YFSVL_GetEndpoints(&ctx->vl_params, &filter,
+ &server->uuid, &uniquifier,
+ &fsEndpoints, &server->fs_nr_addrs,
+ &volEndpoints, &server->vs_nr_addrs,
+ &ctx->result)) {
+ put_kafs_fileserver(server);
+ return rxrpc_call_failed(&ctx->result), NULL;
+ }
+
+ server->fs_addrs = kafs_endpoints_to_addrlist(ctx, fsEndpoints, server->fs_nr_addrs,
+ FS_SERVICE);
+ if (!server->fs_addrs)
+ goto out;
+ server->vs_addrs = kafs_endpoints_to_addrlist(ctx, volEndpoints, server->vs_nr_addrs,
+ VOLSERVICE_PORT);
+ if (!server->vs_addrs)
+ goto out;
+
+ server->inited = true;
+ ok = true;
+ goto out;
+
+fallback_getaddrsu:
+ /* Fall back to the AFS VL.GetAddrsU RPC */
+ memset(&laba, 0, sizeof(laba));
+ laba.Mask = VLADDR_INDEX;
+ laba.index = server_index;
+
+ if (!VL_GetAddrsU(&ctx->vl_params, &laba,
+ &a_uuid, &uniquifier, &nentries,
+ &blkaddrs, &server->fs_nr_addrs, &ctx->result))
+ return rxrpc_call_failed(&ctx->result), NULL;
+
+ afsUUID_to_uuid(&server->uuid, &a_uuid);
+
+ server->fs_addrs = kafs_addrs_to_addrlist(ctx, blkaddrs, server->fs_nr_addrs,
+ FS_PORT, FS_SERVICE);
+ if (!server->fs_addrs)
+ goto out;
+ server->vs_addrs = kafs_addrs_to_addrlist(ctx, blkaddrs, server->vs_nr_addrs,
+ VOLSERVICE_PORT, VOLSERVICE_ID);
+ if (!server->vs_addrs)
+ goto out;
+
+ server->inited = true;
+ ok = true;
+
+out:
+ rxgen_bulk_free_endpoint(fsEndpoints, server->fs_nr_addrs);
+ rxgen_bulk_free_endpoint(volEndpoints, server->vs_nr_addrs);
+ free(blkaddrs);
+ if (!ok)
+ put_kafs_fileserver(server);
+ return ok ? server : NULL;
+}
+
+/**
+ * kafs_look_up_fileserver_by_uuid - Get a server by UUID
+ * @ctx: The cell and authentication context
+ * @uuid: The UUID of the file/volume server
+ *
+ * Look up a server record by UUID. The server record may be retrieved from
+ * the cache, and if not, the result will be cached.
+ */
+struct kafs_fileserver *kafs_look_up_fileserver_by_uuid(struct kafs_context *ctx,
+ const uuid_t *uuid)
+{
+ struct kafs_fileserver *server;
+
+ server = kafs_cache_fileserver(ctx, uuid, NULL, kafs_cmp_fs_uuid);
+ if (server)
+ return server;
+
+ server = kafs_VL_GetAddrs_by_uuid(ctx, uuid);
+ if (!server)
+ return false;
+
+ return kafs_cache_fileserver(ctx, server, server, kafs_cmp_fs);
+}
+
+/**
+ * kafs_look_up_fileserver_by_addr - Get a server by fileserver endpoint address
+ * @ctx: The cell and authentication context
+ * @addr: One of the server's endpoint addresses.
+ *
+ * Look up a server record by the address of one of the fileserver endpoints.
+ * The server record may be retrieved from the cache, and if not, the result
+ * will be cached.
+ */
+struct kafs_fileserver *kafs_look_up_fileserver_by_addr(struct kafs_context *ctx,
+ const struct sockaddr_rxrpc *addr)
+{
+ struct kafs_fileserver *server;
+
+ server = kafs_cache_fileserver(ctx, addr, NULL, kafs_cmp_fs_addr);
+ if (server)
+ return server;
+
+ server = kafs_VL_GetAddrs_by_addr(ctx, addr);
+ if (!server)
+ return false;
+
+ return kafs_cache_fileserver(ctx, server, server, kafs_cmp_fs);
+}
+
+/**
+ * kafs_look_up_fileserver_by_index - Get a fileserver by index
+ * @ctx: The cell and authentication context
+ * @server_index: The index number of the server within the cell
+ *
+ * Look up a fileserver record by index. The server record is cached.
+ */
+struct kafs_fileserver *kafs_look_up_fileserver_by_index(struct kafs_context *ctx,
+ int server_index)
+{
+ struct kafs_fileserver *server;
+
+ server = kafs_VL_GetAddrs_by_index(ctx, server_index);
+ if (!server)
+ return false;
+
+ return kafs_cache_fileserver(ctx, server, server, kafs_cmp_fs);
+}
+
+/**
+ * kafs_map_volume_sites - Map volume sites to servers
+ * @ctx: The cell and authentication context
+ * @vldb: The volume location entry
+ *
+ * Map the locations of a group of volumes, as specified by @vldb, to the file
+ * and volume servers where the data is stored.
+ */
+bool kafs_map_volume_sites(struct kafs_context *ctx, struct kafs_vldb_entry *vldb)
+{
+ struct kafs_fileserver *server;
+ struct sockaddr_rxrpc srx;
+ int i;
+
+ _enter("%s", vldb->name);
+
+ for (i = 0; i < vldb->nr_sites; i++) {
+ struct kafs_vldb_site *site = &vldb->sites[i];
+
+ if (site->server)
+ continue;
+
+ if (site->has_uuid) {
+ server = kafs_look_up_fileserver_by_uuid(ctx, &site->uuid);
+ if (!server)
+ return false;
+ site->server = server;
+
+ } else if (site->has_addr_number) {
+ server = kafs_cache_fileserver(ctx, &site->addr_number, NULL,
+ kafs_cmp_fs_addr);
+ if (server) {
+ site->server = server;
+ continue;
+ }
+
+ memset(&srx, 0, sizeof(srx));
+ srx.transport.family = AF_INET;
+ memcpy(&srx.transport.sin.sin_addr, &site->addr_number, 4);
+ server = kafs_VL_GetAddrs_by_addr(ctx, &srx);
+ if (!server)
+ return false;
+
+ site->server = server;
+ }
+ }
+
+ _leave(" = t");
+ return true;
+}
--- /dev/null
+/* Volume Location 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 "vlservice.h"
+#include "afs_xg.h"
+
+/**
+ * kafs_open_vl_service - Open a Volume Location service for access.
+ * @ctx: The cell and authentication context
+ *
+ * Open the Volume Location service of the cell defined by the context for
+ * access.
+ */
+bool kafs_open_vl_service(struct kafs_context *ctx)
+{
+ if (!ctx->cell_db)
+ return kafs_error(ctx, "Unconfigured cell");
+
+ if (!kafs_open_endpoint(ctx))
+ return false;
+ return true;
+}
+
+/**
+ * kafs_probe_vl_service - Probe a Volume Location service for capabilities
+ * @ctx: The cell and authentication context
+ *
+ * Probe the VL service to find which servers are accessible, what they support
+ * and what variety of server is there.
+ */
+bool kafs_probe_vl_service(struct kafs_context *ctx)
+{
+ struct kafs_cell *cell = ctx->cell_db;
+ struct kafs_server_addr *addrs, *addr;
+ struct kafs_server_list *vlservers = cell->vlservers;
+ struct kafs_server *servers, *server;
+ unsigned char *map;
+ unsigned int *caps, nr_caps;
+ unsigned int i, pref;
+
+ _enter("");
+
+ if (!kafs_open_vl_service(ctx))
+ return false;
+
+ if (!vlservers || vlservers->nr_servers == 0)
+ return kafs_error(ctx, "Cell had no VL servers");
+
+ map = alloca(vlservers->nr_servers);
+ memset(map, 0, vlservers->nr_servers);
+ servers = vlservers->servers;
+
+try_next_server:
+ _debug("next server");
+ server = NULL;
+ pref = UINT_MAX;
+ for (i = 0; i < vlservers->nr_servers; i++) {
+ if (map[i])
+ continue;
+ if (!server || servers[i].pref < pref) {
+ pref = servers[i].pref;
+ server = &servers[i];
+ }
+ }
+
+ if (!server) {
+ if (!ctx->result.source)
+ return kafs_error(ctx, "No VL servers available");
+ return false;
+ }
+
+ addrs = server->addrs;
+ if (!addrs)
+ return kafs_error(ctx, "VL server has no addresses");
+ addr = &addrs[0];
+
+try_next_address:
+ _debug("addr %lu", addrs - addr);
+ memset(&ctx->vl_params, 0, sizeof(ctx->vl_params));
+ ctx->vl_params.endpoint = ctx->endpoint;
+ ctx->vl_params.security = ctx->security;
+ ctx->vl_params.peer.srx_family = AF_RXRPC;
+ ctx->vl_params.peer.srx_service = VL_SERVICE;
+ ctx->vl_params.peer.transport_type = SOCK_DGRAM;
+ ctx->vl_params.upgrade_service = true;
+
+ switch (addr->sin.sin_family) {
+ case AF_INET:
+ memcpy(&ctx->vl_params.peer.transport, &addr->sin, sizeof(addr->sin));
+ if (!ctx->vl_params.peer.transport.sin.sin_port)
+ ctx->vl_params.peer.transport.sin.sin_port = htons(VL_PORT);
+ ctx->vl_params.peer.transport_len = sizeof(addr->sin);
+ ctx->vl_params.peer_len = sizeof(ctx->vl_params.peer);
+ break;
+ case AF_INET6:
+ memcpy(&ctx->vl_params.peer.transport, &addr->sin6, sizeof(addr->sin6));
+ if (!ctx->vl_params.peer.transport.sin6.sin6_port)
+ ctx->vl_params.peer.transport.sin6.sin6_port = htons(VL_PORT);
+ ctx->vl_params.peer.transport_len = sizeof(addr->sin6);
+ ctx->vl_params.peer_len = sizeof(ctx->vl_params.peer);
+ break;
+ default:
+ return kafs_error(ctx, "Unsupported address family");
+ }
+
+ if (!VL_GetCapabilities(&ctx->vl_params, &caps, &nr_caps, &ctx->result)) {
+ switch (ctx->result.source) {
+ case rxrpc_error_remote_abort:
+ switch (ctx->result.abort_code) {
+ case RXGEN_OPCODE:
+ ctx->vl_service = kafs_service_afs;
+ ctx->vl_params.upgrade_service = false;
+ memset(&ctx->vl_caps, 0, sizeof(ctx->vl_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++;
+ if (addr - addrs >= server->nr_addrs) {
+ map[server - servers] = 1;
+ goto try_next_server;
+ }
+ goto try_next_address;
+ default:
+ return false;
+ }
+
+ default:
+ return false;
+ }
+
+ }
+
+ ctx->vl_params.upgrade_service = false;
+ ctx->vl_server = server;
+ ctx->vl_addr = addr - addrs;
+ switch (ctx->result.service_id) {
+ case VL_SERVICE:
+ ctx->vl_service = kafs_service_afs;
+ break;
+ case YFS_VL_SERVICE:
+ ctx->vl_service = kafs_service_yfs;
+ ctx->vl_params.peer.srx_service = ctx->result.service_id;
+ break;
+ default:
+ return kafs_error(ctx, "Unsupported VL service %u", ctx->result.service_id);
+ }
+
+ memset(&ctx->vl_caps, 0, sizeof(ctx->vl_caps));
+ nr_caps *= sizeof(unsigned int);
+ if (nr_caps > sizeof(ctx->vl_caps))
+ nr_caps = sizeof(ctx->vl_caps);
+ memcpy(&ctx->vl_caps, caps, nr_caps);
+ free(caps);
+
+out:
+ if (kafs_debug_caps) {
+ printf("vl-caps:");
+ for (i = 0; i < sizeof(ctx->vl_caps) / sizeof(unsigned int); i++)
+ printf(" %08x", ctx->vl_caps[i]);
+ if (ctx->vl_service == kafs_service_yfs)
+ printf(" yfs\n");
+ else
+ printf(" afs\n");
+ }
+
+ _leave(" = t");
+ return true;
+}
--- /dev/null
+/* Volume Location handling
+ *
+ * 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 "vlservice.h"
+#include "afs_xg.h"
+
+/**
+ * free_kafs_vldb_entry - Free a VLDB entry
+ * @vldb: The entry to free
+ */
+void free_kafs_vldb_entry(struct kafs_vldb_entry *vldb)
+{
+ int i;
+
+ if (vldb) {
+ struct kafs_vldb_site *sites = vldb->sites;
+
+ for (i = 0; i < vldb->nr_sites; i++) {
+ put_kafs_fileserver(sites[i].server);
+ }
+ free(vldb->sites);
+ free(vldb);
+ }
+}
+
+/**
+ * clear_kafs_vldb_entry - Clear a VLDB entry list
+ * @vldb: The entry to clear
+ *
+ * Clear a VLDB entry list, releasing all the volumes it points to, but only
+ * resetting and not freeing the list itself..
+ */
+void clear_kafs_vldb_entry_list(struct kafs_vldb_entry_list *vlist)
+{
+ struct kafs_vldb_entry **list = vlist->entries;
+ int i;
+
+ for (i = 0; i < vlist->nr_entries; i++)
+ free_kafs_vldb_entry(list[i]);
+ free(list);
+
+ vlist->nr_entries = 0;
+ vlist->entries = NULL;
+}
+
+/**
+ * free_kafs_vldb_entry - Free a VLDB entry list
+ * @list: The entry to free
+ *
+ * Clear a VLDB entry list, releasing all the volumes it points to, but only
+ * resetting and not freeing the list itself..
+ */
+void free_kafs_vldb_entry_list(struct kafs_vldb_entry_list *vlist)
+{
+ if (vlist) {
+ clear_kafs_vldb_entry_list(vlist);
+ free(vlist);
+ }
+}
+
+/*
+ * Expand a volume list by a number of entries.
+ */
+static bool kafs_expand_vldb_entry_list(struct kafs_context *ctx,
+ struct kafs_vldb_entry_list *vlist,
+ size_t add_entries)
+{
+ struct kafs_vldb_entry **list = vlist->entries;
+ size_t max;
+
+ if (vlist->nr_entries + add_entries <= vlist->max_entries)
+ return true;
+
+ max = vlist->nr_entries + add_entries + 1;
+ list = reallocarray(list, max, sizeof(list[0]));
+ if (!list)
+ return kafs_nomem(ctx);
+
+ memset(list + vlist->max_entries, 0, (max - vlist->max_entries) * sizeof(list[0]));
+ vlist->max_entries = max - 1;
+ vlist->entries = list;
+ return true;
+}
+
+/*
+ * Allocate a blank volume record.
+ */
+static struct kafs_vldb_entry *kafs_alloc_vldb_entry(struct kafs_context *ctx, size_t nr_sites)
+{
+ struct kafs_vldb_site *sites;
+ struct kafs_vldb_entry *vldb;
+
+ vldb = calloc(1, sizeof(*vldb));
+ if (!vldb)
+ return kafs_nomem(ctx), NULL;
+
+ sites = calloc(NMAXNSERVERS, sizeof(*sites));
+ if (!sites) {
+ free(vldb);
+ return kafs_nomem(ctx), NULL;
+ }
+
+ vldb->sites = sites;
+ return vldb;
+}
+
+/*
+ * Transcribe the information from an array of nvldbentry records into
+ * additional records in a volume list.
+ */
+static bool kafs_nentries_to_vldb_entries(struct kafs_context *ctx,
+ size_t nr_entries,
+ struct nvldbentry **entries,
+ struct kafs_vldb_entry_list *vlist)
+{
+ int i, j;
+
+ if (!kafs_expand_vldb_entry_list(ctx, vlist, nr_entries))
+ return false;
+
+ for (i = 0; i < nr_entries; i++) {
+ struct kafs_vldb_entry *vldb;
+ struct nvldbentry *entry = entries[i];
+
+ vldb = kafs_alloc_vldb_entry(ctx, NMAXNSERVERS);
+ if (!vldb)
+ return false;
+
+ strcpy(vldb->name, entry->name);
+ vldb->nr_sites = entry->nServers;
+ vldb->clone_id = entry->cloneId;
+ vldb->flags = entry->flags;
+ vldb->match_index = entry->matchindex;
+ vldb->has_match_index = true;
+
+ for (j = 0; j < MAXTYPES; j++)
+ vldb->volume_id[j] = entry->volumeId[j];
+
+ for (j = 0; j < NMAXNSERVERS; j++) {
+ struct kafs_vldb_site *site = &vldb->sites[j];
+ site->partition.id = entry->serverPartition[j];
+ site->flags = entry->serverFlags[j];
+ site->addr_number = htonl(entry->serverNumber[j]);
+ site->has_addr_number = true;
+ }
+
+ vlist->entries[vlist->nr_entries++] = vldb;
+ }
+
+ _leave(" = t");
+ return true;
+}
+
+/*
+ * Transcribe the information from an array of uvldbentry records into
+ * additional records in a volume list.
+ */
+static bool kafs_uentries_to_vldb_entries(struct kafs_context *ctx,
+ size_t nr_entries,
+ struct uvldbentry **entries,
+ struct kafs_vldb_entry_list *vlist)
+{
+ int i, j;
+
+ if (!kafs_expand_vldb_entry_list(ctx, vlist, nr_entries))
+ return false;
+
+ for (i = 0; i < nr_entries; i++) {
+ struct kafs_vldb_entry *vldb;
+ struct uvldbentry *entry = entries[i];
+
+ vldb = kafs_alloc_vldb_entry(ctx, NMAXNSERVERS);
+ if (!vldb)
+ return false;
+
+ strcpy(vldb->name, entry->name);
+ vldb->nr_sites = entry->nServers;
+ vldb->clone_id = entry->cloneId;
+ vldb->flags = entry->flags;
+ vldb->match_index = entry->matchindex;
+ vldb->has_match_index = true;
+
+ for (j = 0; j < MAXTYPES; j++)
+ vldb->volume_id[j] = entry->volumeId[j];
+
+ for (j = 0; j < NMAXNSERVERS; j++) {
+ struct kafs_vldb_site *site = &vldb->sites[j];
+
+ afsUUID_to_uuid(&site->uuid, &entry->serverNumber[j]);
+ site->has_uuid = true;
+ site->partition.id = entry->serverPartition[j];
+ site->flags = entry->serverFlags[j];
+ }
+
+ vlist->entries[vlist->nr_entries++] = vldb;
+ }
+
+ _leave(" = t");
+ return true;
+}
+
+/*
+ * Transcribe the information from an vldbentry64 record into a kafs_volume
+ * record and add it into a volume list.
+ */
+static bool kafs_entries64_to_volumes(struct kafs_context *ctx,
+ size_t nr_entries,
+ struct vldbentry64 **entries,
+ struct kafs_vldb_entry_list *vlist)
+{
+ int i, j;
+
+ if (!kafs_expand_vldb_entry_list(ctx, vlist, nr_entries))
+ return false;
+
+ for (i = 0; i < nr_entries; i++) {
+ struct kafs_vldb_entry *vldb;
+ struct vldbentry64 *entry = entries[i];
+
+ vldb = kafs_alloc_vldb_entry(ctx, NMAXNSERVERS);
+ if (!vldb)
+ return false;
+
+ strcpy(vldb->name, entry->name);
+ vldb->nr_sites = entry->locations__nr;
+ vldb->clone_id = entry->cloneId;
+ vldb->flags = entry->flags;
+
+ for (j = 0; j < MAXTYPES; j++)
+ vldb->volume_id[j] = entry->volumeId[j];
+
+ for (j = 0; j < entry->locations__nr; j++) {
+ struct vldbentry64_location *yel = &entry->locations[j];
+ struct kafs_vldb_site *site = &vldb->sites[j];
+
+ afsUUID_to_uuid(&site->uuid, &yel->uuid);
+ site->has_uuid = true;
+ site->unique = yel->unique;
+ site->has_unique = true;
+ site->partition.id = yel->partition;
+ site->flags = yel->flags;
+ }
+
+ vlist->entries[vlist->nr_entries++] = vldb;
+ }
+
+ return true;
+}
+
+/**
+ * kafs_VL_GetEntryByName - Query the VLDB for a volume by name.
+ * @ctx: The cell and authentication context
+ * @name: The name or ID of the volume
+ * @vlist: The record list in which to store the volume record
+ *
+ * Query the Volume Location service to find out the details of a volume group
+ * and the list of servers on which it resides. The volume is an ID if it
+ * begins with a numeric digit and a name otherwise.
+ *
+ * If successful, the resulting record is appended to the list.
+ */
+bool kafs_VL_GetEntryByName(struct kafs_context *ctx,
+ const char *name,
+ struct kafs_vldb_entry_list *vlist)
+{
+ struct vldbentry64 entry64, *entries64[1] = { &entry64 };
+ struct uvldbentry uventry, *uventries[1] = { &uventry };
+
+ _enter("%s", name);
+
+ if (!kafs_probe_vl_service(ctx))
+ return false;
+
+ if (ctx->vl_service == kafs_service_yfs) {
+ if (!YFSVL_GetEntryByName64(&ctx->vl_params, name, VLGET_FS_UUID,
+ &entry64, &ctx->result))
+ return rxrpc_call_failed(&ctx->result);
+
+ return kafs_entries64_to_volumes(ctx, 1, entries64, vlist);
+ }
+
+ if (!VL_GetEntryByNameU(&ctx->vl_params, name, &uventry, &ctx->result))
+ return rxrpc_call_failed(&ctx->result);
+
+ return kafs_uentries_to_vldb_entries(ctx, 1, uventries, vlist);
+}
+
+/**
+ * kafs_look_up_volume_by_name - Look up a volume by name.
+ * @ctx: The cell and authentication context
+ * @name: The name or ID of the volume
+ * @vlist: The record list in to which the volume record will be appended
+ *
+ * Look up a record of a volume by name to retrieve the details of a volume and
+ * the list of servers on which it resides. The volume is an ID if it begins
+ * with a numeric digit and a name otherwise.
+ *
+ * If successful, the resulting record is appended to the list. The record may
+ * be cached locally.
+ */
+bool kafs_look_up_volume_by_name(struct kafs_context *ctx,
+ struct kafs_volume_spec *name,
+ struct kafs_vldb_entry_list *vlist)
+{
+ struct kafs_vldb_entry *vldb;
+
+ _enter("");
+
+ if (!kafs_VL_GetEntryByName(ctx, name->name, vlist))
+ return false;
+
+ vldb = vlist->entries[vlist->nr_entries - 1];
+ if (!kafs_map_volume_sites(ctx, vldb))
+ return false;
+
+ _leave(" = t");
+ return true;
+}
+
+/**
+ * kafs_VL_ListAttributes - Query the VLDB for a list of matching volumes.
+ * @ctx: The cell and authentication context
+ * @attributes: The attributes by which to filter the volume list
+ * @volume_name_pattern: An RE that is used to filter the volume list (or NULL)
+ * @vlist: The record list in to which volume records will be appended
+ *
+ * Query the Volume Location service to retrieve the details of a set of
+ * volumes, as filtered by the attribute set and, optionally, a name pattern.
+ *
+ * If successful, the resulting records are appended to the list.
+ */
+bool kafs_VL_ListAttributes(struct kafs_context *ctx,
+ struct VldbListByAttributes *attributes,
+ const char *volume_name_pattern,
+ struct kafs_vldb_entry_list *vlist)
+{
+ struct uvldbentry **uentries;
+ struct nvldbentry **nentries;
+ struct vldbentry **entries;
+ unsigned int nr_entries;
+ int32_t entry_count, pos, next_pos;
+ bool ret;
+
+ _enter("%x", attributes->Mask);
+
+ if (!kafs_probe_vl_service(ctx))
+ return false;
+
+ /* Start off by trying the YFS ListAttributesU2 RPC */
+ if (ctx->vl_service == kafs_service_yfs) {
+ pos = 0;
+ next_pos = -1;
+ do {
+ if (YFSVL_ListAttributesU2(&ctx->vl_params, attributes,
+ volume_name_pattern ?: "", pos,
+ &entry_count, &uentries, &nr_entries,
+ &next_pos, &ctx->result)
+ ) {
+ ret = kafs_uentries_to_vldb_entries(ctx, nr_entries, uentries,
+ vlist);
+ rxgen_bulk_free_uvldbentry(uentries, nr_entries);
+ if (!ret)
+ return ret;
+ pos = next_pos;
+ }
+ } while (next_pos != -1 && ctx->result.source == rxrpc_error_none);
+
+ if (ctx->result.source == rxrpc_error_none)
+ return true;
+ if (ctx->result.source == rxrpc_error_remote_abort &&
+ ctx->result.abort_code != RXGEN_OPCODE)
+ return false;
+ }
+
+ /* Fall back to the ListAttributesN2 RPC */
+ pos = 0;
+ next_pos = -1;
+ do {
+ if (VL_ListAttributesN2(&ctx->vl_params, attributes,
+ volume_name_pattern ?: "", pos,
+ &entry_count, &nentries, &nr_entries,
+ &next_pos, &ctx->result)) {
+ ret = kafs_nentries_to_vldb_entries(ctx, nr_entries, nentries, vlist);
+ rxgen_bulk_free_nvldbentry(nentries, nr_entries);
+ if (!ret)
+ return ret;
+ pos = next_pos;
+ }
+ } while (next_pos != -1 && ctx->result.source == rxrpc_error_none);
+
+ if (ctx->result.source == rxrpc_error_none)
+ return true;
+ if (ctx->result.source == rxrpc_error_remote_abort &&
+ ctx->result.abort_code != RXGEN_OPCODE)
+ return false;
+
+ /* Fall back to the ListAttributesN RPC */
+ if (VL_ListAttributesN(&ctx->vl_params, attributes, &entry_count,
+ &nentries, &nr_entries,
+ &ctx->result)) {
+ ret = kafs_nentries_to_vldb_entries(ctx, nr_entries, nentries, vlist);
+ rxgen_bulk_free_nvldbentry(nentries, nr_entries);
+ return ret;
+ }
+
+ if (ctx->result.source == rxrpc_error_remote_abort &&
+ ctx->result.abort_code != RXGEN_OPCODE)
+ return false;
+
+ /* Fall back to the ListAttributes RPC */
+ if (VL_ListAttributes(&ctx->vl_params, attributes, &entry_count,
+ &entries, &nr_entries,
+ &ctx->result)) {
+ rxgen_bulk_free_vldbentry(entries, nr_entries);
+ return false;
+ }
+
+ _leave(" = f");
+ return false;
+}
+
+/**
+ * kafs_look_up_volume_by_name - Look up a volume by attributes and/or name pattern.
+ * @ctx: The cell and authentication context
+ * @fsspec: A fileserver address filter (or NULL)
+ * @partition: A partition filter (or NULL)
+ * @locked: A filter on the volume-locked flag
+ * @volume_name_pattern: An RE filter on the volume name (or NULL)
+ * @vlist: The record list in to which the volume record will be appended
+ *
+ * Look up a record of a volume by some combination of server address,
+ * partition ID, volume locked flag and a RE volume name pattern to retrieve
+ * the details of a volume and the list of servers on which it resides.
+ *
+ * If successful, the resulting records are appended to the list. These
+ * records may be cached locally.
+ */
+bool kafs_look_up_vlist_by_attributes(struct kafs_context *ctx,
+ struct kafs_fileserver_spec *fsspec,
+ struct kafs_partition *partition,
+ bool locked,
+ const char *volume_name_pattern,
+ struct kafs_vldb_entry_list *vlist)
+{
+ struct VldbListByAttributes attr = { .Mask = 0 };
+ struct sockaddr_in *sin;
+ int i;
+
+ if (fsspec) {
+ if (!fsspec->nr_addrs)
+ return kafs_error(ctx, "Unspecified fileserver address");
+ for (i = 0; i < fsspec->nr_addrs; i++) {
+ sin = &fsspec->addrs[i].transport.sin;
+ if (sin->sin_family == AF_INET)
+ break;
+ }
+ if (i > fsspec->nr_addrs)
+ return kafs_error(ctx, "Filter only supports an IPv4 address");
+ attr.Mask |= VLLIST_SERVER;
+
+ /* The XDR encoder will pass attr.server through htonl() */
+ attr.server = ntohl(sin->sin_addr.s_addr);
+ }
+
+ if (partition) {
+ attr.Mask |= VLLIST_PARTITION;
+ attr.partition = partition->id;
+ }
+ if (locked) {
+ attr.Mask |= VLLIST_FLAG;
+ attr.flag = VLOP_MOVE | VLOP_RELEASE | VLOP_BACKUP | VLOP_DELETE | VLOP_DUMP;
+ }
+
+ if (!kafs_VL_ListAttributes(ctx, &attr, volume_name_pattern, vlist))
+ return false;
+
+ for (i = 0; i < vlist->nr_entries; i++)
+ if (!kafs_map_volume_sites(ctx, vlist->entries[i]))
+ return false;
+
+ return true;
+}
--- /dev/null
+/* KAFS VL 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 VLSERVICE_H
+#define VLSERVICE_H
+
+#include <uuid/uuid.h>
+#include "rxrpc.h"
+#include "kafs.h"
+
+struct kafs_vldb_site {
+ struct kafs_fileserver *server;
+ uuid_t uuid;
+ unsigned int unique;
+ unsigned int flags;
+ unsigned int nr_addrs;
+ unsigned int addr_number;
+ bool has_uuid:1;
+ bool has_unique:1;
+ bool has_addr_number:1;
+ struct kafs_partition partition;
+};
+
+struct kafs_vldb_entry {
+ char name[512 + 1];
+ kafs_volume_id_t volume_id[3];
+ unsigned int clone_id;
+ unsigned int flags;
+ unsigned int match_index;
+ bool has_match_index:1;
+ unsigned int nr_sites;
+ struct kafs_vldb_site *sites;
+};
+
+struct kafs_vldb_entry_list {
+ unsigned int nr_entries;
+ unsigned int max_entries;
+ struct kafs_vldb_entry **entries;
+};
+
+
+/*
+ * vl_fileservers.c
+ */
+extern void afsUUID_to_uuid(uuid_t *uuid, const struct afsUUID *au);
+extern void uuid_to_afsUUID(struct afsUUID *au, const uuid_t *uuid);
+extern struct kafs_fileserver *kafs_look_up_fileserver_by_uuid(struct kafs_context *ctx,
+ const uuid_t *uuid);
+extern struct kafs_fileserver *kafs_look_up_fileserver_by_addr(struct kafs_context *ctx,
+ const struct sockaddr_rxrpc *addr);
+extern struct kafs_fileserver *kafs_look_up_fileserver_by_index(struct kafs_context *ctx,
+ int server_index);
+extern bool kafs_map_volume_sites(struct kafs_context *ctx, struct kafs_vldb_entry *vldb);
+
+/*
+ * vl_probe.c
+ */
+extern bool kafs_open_vl_service(struct kafs_context *ctx);
+extern bool kafs_probe_vl_service(struct kafs_context *ctx);
+
+/*
+ * vl_volumes.c
+ */
+extern void free_kafs_vldb_entry(struct kafs_vldb_entry *);
+extern void clear_kafs_vldb_entry_list(struct kafs_vldb_entry_list *);
+extern void free_kafs_volume_list(struct kafs_vldb_entry_list *);
+
+extern bool kafs_look_up_volume_by_name(struct kafs_context *ctx,
+ struct kafs_volume_spec *name,
+ struct kafs_vldb_entry_list *volumes);
+
+extern bool kafs_look_up_volumes_by_attributes(struct kafs_context *ctx,
+ struct kafs_fileserver_spec *fileserver,
+ struct kafs_partition *partition,
+ bool locked,
+ const char *volume_name_pattern,
+ struct kafs_vldb_entry_list *volumes);
+
+#endif /* VLSERVICE_H */