| /* AFS Volume Location Service client |
| * |
| * Copyright (C) 2002 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 License |
| * as published by the Free Software Foundation; either version |
| * 2 of the License, or (at your option) any later version. |
| */ |
| |
| #include <linux/gfp.h> |
| #include <linux/init.h> |
| #include <linux/sched.h> |
| #include "afs_fs.h" |
| #include "internal.h" |
| |
| /* |
| * Deliver reply data to a VL.GetEntryByNameU call. |
| */ |
| static int afs_deliver_vl_get_entry_by_name_u(struct afs_call *call) |
| { |
| struct afs_uvldbentry__xdr *uvldb; |
| struct afs_vldb_entry *entry; |
| bool new_only = false; |
| u32 tmp, nr_servers, vlflags; |
| int i, ret; |
| |
| _enter(""); |
| |
| ret = afs_transfer_reply(call); |
| if (ret < 0) |
| return ret; |
| |
| /* unmarshall the reply once we've received all of it */ |
| uvldb = call->buffer; |
| entry = call->reply[0]; |
| |
| nr_servers = ntohl(uvldb->nServers); |
| if (nr_servers > AFS_NMAXNSERVERS) |
| nr_servers = AFS_NMAXNSERVERS; |
| |
| for (i = 0; i < ARRAY_SIZE(uvldb->name) - 1; i++) |
| entry->name[i] = (u8)ntohl(uvldb->name[i]); |
| entry->name[i] = 0; |
| entry->name_len = strlen(entry->name); |
| |
| /* If there is a new replication site that we can use, ignore all the |
| * sites that aren't marked as new. |
| */ |
| for (i = 0; i < nr_servers; i++) { |
| tmp = ntohl(uvldb->serverFlags[i]); |
| if (!(tmp & AFS_VLSF_DONTUSE) && |
| (tmp & AFS_VLSF_NEWREPSITE)) |
| new_only = true; |
| } |
| |
| vlflags = ntohl(uvldb->flags); |
| for (i = 0; i < nr_servers; i++) { |
| struct afs_uuid__xdr *xdr; |
| struct afs_uuid *uuid; |
| int j; |
| |
| tmp = ntohl(uvldb->serverFlags[i]); |
| if (tmp & AFS_VLSF_DONTUSE || |
| (new_only && !(tmp & AFS_VLSF_NEWREPSITE))) |
| continue; |
| if (tmp & AFS_VLSF_RWVOL) { |
| entry->fs_mask[i] |= AFS_VOL_VTM_RW; |
| if (vlflags & AFS_VLF_BACKEXISTS) |
| entry->fs_mask[i] |= AFS_VOL_VTM_BAK; |
| } |
| if (tmp & AFS_VLSF_ROVOL) |
| entry->fs_mask[i] |= AFS_VOL_VTM_RO; |
| if (!entry->fs_mask[i]) |
| continue; |
| |
| xdr = &uvldb->serverNumber[i]; |
| uuid = (struct afs_uuid *)&entry->fs_server[i]; |
| uuid->time_low = xdr->time_low; |
| uuid->time_mid = htons(ntohl(xdr->time_mid)); |
| uuid->time_hi_and_version = htons(ntohl(xdr->time_hi_and_version)); |
| uuid->clock_seq_hi_and_reserved = (u8)ntohl(xdr->clock_seq_hi_and_reserved); |
| uuid->clock_seq_low = (u8)ntohl(xdr->clock_seq_low); |
| for (j = 0; j < 6; j++) |
| uuid->node[j] = (u8)ntohl(xdr->node[j]); |
| |
| entry->nr_servers++; |
| } |
| |
| for (i = 0; i < AFS_MAXTYPES; i++) |
| entry->vid[i] = ntohl(uvldb->volumeId[i]); |
| |
| if (vlflags & AFS_VLF_RWEXISTS) |
| __set_bit(AFS_VLDB_HAS_RW, &entry->flags); |
| if (vlflags & AFS_VLF_ROEXISTS) |
| __set_bit(AFS_VLDB_HAS_RO, &entry->flags); |
| if (vlflags & AFS_VLF_BACKEXISTS) |
| __set_bit(AFS_VLDB_HAS_BAK, &entry->flags); |
| |
| if (!(vlflags & (AFS_VLF_RWEXISTS | AFS_VLF_ROEXISTS | AFS_VLF_BACKEXISTS))) { |
| entry->error = -ENOMEDIUM; |
| __set_bit(AFS_VLDB_QUERY_ERROR, &entry->flags); |
| } |
| |
| __set_bit(AFS_VLDB_QUERY_VALID, &entry->flags); |
| _leave(" = 0 [done]"); |
| return 0; |
| } |
| |
| static void afs_destroy_vl_get_entry_by_name_u(struct afs_call *call) |
| { |
| kfree(call->reply[0]); |
| afs_flat_call_destructor(call); |
| } |
| |
| /* |
| * VL.GetEntryByNameU operation type. |
| */ |
| static const struct afs_call_type afs_RXVLGetEntryByNameU = { |
| .name = "VL.GetEntryByNameU", |
| .op = afs_VL_GetEntryByNameU, |
| .deliver = afs_deliver_vl_get_entry_by_name_u, |
| .destructor = afs_destroy_vl_get_entry_by_name_u, |
| }; |
| |
| /* |
| * Dispatch a get volume entry by name or ID operation (uuid variant). If the |
| * volname is a decimal number then it's a volume ID not a volume name. |
| */ |
| struct afs_vldb_entry *afs_vl_get_entry_by_name_u(struct afs_vl_cursor *vc, |
| const char *volname, |
| int volnamesz) |
| { |
| struct afs_vldb_entry *entry; |
| struct afs_call *call; |
| struct afs_net *net = vc->cell->net; |
| size_t reqsz, padsz; |
| __be32 *bp; |
| |
| _enter(""); |
| |
| padsz = (4 - (volnamesz & 3)) & 3; |
| reqsz = 8 + volnamesz + padsz; |
| |
| entry = kzalloc(sizeof(struct afs_vldb_entry), GFP_KERNEL); |
| if (!entry) |
| return ERR_PTR(-ENOMEM); |
| |
| call = afs_alloc_flat_call(net, &afs_RXVLGetEntryByNameU, reqsz, |
| sizeof(struct afs_uvldbentry__xdr)); |
| if (!call) { |
| kfree(entry); |
| return ERR_PTR(-ENOMEM); |
| } |
| |
| call->key = vc->key; |
| call->reply[0] = entry; |
| call->ret_reply0 = true; |
| |
| /* Marshall the parameters */ |
| bp = call->request; |
| *bp++ = htonl(VLGETENTRYBYNAMEU); |
| *bp++ = htonl(volnamesz); |
| memcpy(bp, volname, volnamesz); |
| if (padsz > 0) |
| memset((void *)bp + volnamesz, 0, padsz); |
| |
| trace_afs_make_vl_call(call); |
| return (struct afs_vldb_entry *)afs_make_call(&vc->ac, call, GFP_KERNEL, false); |
| } |
| |
| /* |
| * Deliver reply data to a VL.GetAddrsU call. |
| * |
| * GetAddrsU(IN ListAddrByAttributes *inaddr, |
| * OUT afsUUID *uuidp1, |
| * OUT uint32_t *uniquifier, |
| * OUT uint32_t *nentries, |
| * OUT bulkaddrs *blkaddrs); |
| */ |
| static int afs_deliver_vl_get_addrs_u(struct afs_call *call) |
| { |
| struct afs_addr_list *alist; |
| __be32 *bp; |
| u32 uniquifier, nentries, count; |
| int i, ret; |
| |
| _enter("{%u,%zu/%u}", |
| call->unmarshall, iov_iter_count(call->_iter), call->count); |
| |
| switch (call->unmarshall) { |
| case 0: |
| afs_extract_to_buf(call, |
| sizeof(struct afs_uuid__xdr) + 3 * sizeof(__be32)); |
| call->unmarshall++; |
| |
| /* Extract the returned uuid, uniquifier, nentries and blkaddrs size */ |
| case 1: |
| ret = afs_extract_data(call, true); |
| if (ret < 0) |
| return ret; |
| |
| bp = call->buffer + sizeof(struct afs_uuid__xdr); |
| uniquifier = ntohl(*bp++); |
| nentries = ntohl(*bp++); |
| count = ntohl(*bp); |
| |
| nentries = min(nentries, count); |
| alist = afs_alloc_addrlist(nentries, FS_SERVICE, AFS_FS_PORT); |
| if (!alist) |
| return -ENOMEM; |
| alist->version = uniquifier; |
| call->reply[0] = alist; |
| call->count = count; |
| call->count2 = nentries; |
| call->unmarshall++; |
| |
| more_entries: |
| count = min(call->count, 4U); |
| afs_extract_to_buf(call, count * sizeof(__be32)); |
| |
| /* Extract entries */ |
| case 2: |
| ret = afs_extract_data(call, call->count > 4); |
| if (ret < 0) |
| return ret; |
| |
| alist = call->reply[0]; |
| bp = call->buffer; |
| count = min(call->count, 4U); |
| for (i = 0; i < count; i++) |
| if (alist->nr_addrs < call->count2) |
| afs_merge_fs_addr4(alist, *bp++, AFS_FS_PORT); |
| |
| call->count -= count; |
| if (call->count > 0) |
| goto more_entries; |
| call->unmarshall++; |
| break; |
| } |
| |
| _leave(" = 0 [done]"); |
| return 0; |
| } |
| |
| static void afs_vl_get_addrs_u_destructor(struct afs_call *call) |
| { |
| afs_put_server(call->net, (struct afs_server *)call->reply[0]); |
| kfree(call->reply[1]); |
| return afs_flat_call_destructor(call); |
| } |
| |
| /* |
| * VL.GetAddrsU operation type. |
| */ |
| static const struct afs_call_type afs_RXVLGetAddrsU = { |
| .name = "VL.GetAddrsU", |
| .op = afs_VL_GetAddrsU, |
| .deliver = afs_deliver_vl_get_addrs_u, |
| .destructor = afs_vl_get_addrs_u_destructor, |
| }; |
| |
| /* |
| * Dispatch an operation to get the addresses for a server, where the server is |
| * nominated by UUID. |
| */ |
| struct afs_addr_list *afs_vl_get_addrs_u(struct afs_vl_cursor *vc, |
| const uuid_t *uuid) |
| { |
| struct afs_ListAddrByAttributes__xdr *r; |
| const struct afs_uuid *u = (const struct afs_uuid *)uuid; |
| struct afs_call *call; |
| struct afs_net *net = vc->cell->net; |
| __be32 *bp; |
| int i; |
| |
| _enter(""); |
| |
| call = afs_alloc_flat_call(net, &afs_RXVLGetAddrsU, |
| sizeof(__be32) + sizeof(struct afs_ListAddrByAttributes__xdr), |
| sizeof(struct afs_uuid__xdr) + 3 * sizeof(__be32)); |
| if (!call) |
| return ERR_PTR(-ENOMEM); |
| |
| call->key = vc->key; |
| call->reply[0] = NULL; |
| call->ret_reply0 = true; |
| |
| /* Marshall the parameters */ |
| bp = call->request; |
| *bp++ = htonl(VLGETADDRSU); |
| r = (struct afs_ListAddrByAttributes__xdr *)bp; |
| r->Mask = htonl(AFS_VLADDR_UUID); |
| r->ipaddr = 0; |
| r->index = 0; |
| r->spare = 0; |
| r->uuid.time_low = u->time_low; |
| r->uuid.time_mid = htonl(ntohs(u->time_mid)); |
| r->uuid.time_hi_and_version = htonl(ntohs(u->time_hi_and_version)); |
| r->uuid.clock_seq_hi_and_reserved = htonl(u->clock_seq_hi_and_reserved); |
| r->uuid.clock_seq_low = htonl(u->clock_seq_low); |
| for (i = 0; i < 6; i++) |
| r->uuid.node[i] = htonl(u->node[i]); |
| |
| trace_afs_make_vl_call(call); |
| return (struct afs_addr_list *)afs_make_call(&vc->ac, call, GFP_KERNEL, false); |
| } |
| |
| /* |
| * Deliver reply data to an VL.GetCapabilities operation. |
| */ |
| static int afs_deliver_vl_get_capabilities(struct afs_call *call) |
| { |
| u32 count; |
| int ret; |
| |
| _enter("{%u,%zu/%u}", |
| call->unmarshall, iov_iter_count(call->_iter), call->count); |
| |
| switch (call->unmarshall) { |
| case 0: |
| afs_extract_to_tmp(call); |
| call->unmarshall++; |
| |
| /* Extract the capabilities word count */ |
| case 1: |
| ret = afs_extract_data(call, true); |
| if (ret < 0) |
| return ret; |
| |
| count = ntohl(call->tmp); |
| call->count = count; |
| call->count2 = count; |
| |
| call->unmarshall++; |
| afs_extract_discard(call, count * sizeof(__be32)); |
| |
| /* Extract capabilities words */ |
| case 2: |
| ret = afs_extract_data(call, false); |
| if (ret < 0) |
| return ret; |
| |
| /* TODO: Examine capabilities */ |
| |
| call->unmarshall++; |
| break; |
| } |
| |
| _leave(" = 0 [done]"); |
| return 0; |
| } |
| |
| static void afs_destroy_vl_get_capabilities(struct afs_call *call) |
| { |
| struct afs_vlserver *server = call->reply[0]; |
| |
| afs_put_vlserver(call->net, server); |
| afs_flat_call_destructor(call); |
| } |
| |
| /* |
| * VL.GetCapabilities operation type |
| */ |
| static const struct afs_call_type afs_RXVLGetCapabilities = { |
| .name = "VL.GetCapabilities", |
| .op = afs_VL_GetCapabilities, |
| .deliver = afs_deliver_vl_get_capabilities, |
| .done = afs_vlserver_probe_result, |
| .destructor = afs_destroy_vl_get_capabilities, |
| }; |
| |
| /* |
| * Probe a volume server for the capabilities that it supports. This can |
| * return up to 196 words. |
| * |
| * We use this to probe for service upgrade to determine what the server at the |
| * other end supports. |
| */ |
| int afs_vl_get_capabilities(struct afs_net *net, |
| struct afs_addr_cursor *ac, |
| struct key *key, |
| struct afs_vlserver *server, |
| unsigned int server_index, |
| bool async) |
| { |
| struct afs_call *call; |
| __be32 *bp; |
| |
| _enter(""); |
| |
| call = afs_alloc_flat_call(net, &afs_RXVLGetCapabilities, 1 * 4, 16 * 4); |
| if (!call) |
| return -ENOMEM; |
| |
| call->key = key; |
| call->reply[0] = afs_get_vlserver(server); |
| call->reply[1] = (void *)(long)server_index; |
| call->upgrade = true; |
| call->want_reply_time = true; |
| |
| /* marshall the parameters */ |
| bp = call->request; |
| *bp++ = htonl(VLGETCAPABILITIES); |
| |
| /* Can't take a ref on server */ |
| trace_afs_make_vl_call(call); |
| return afs_make_call(ac, call, GFP_KERNEL, async); |
| } |
| |
| /* |
| * Deliver reply data to a YFSVL.GetEndpoints call. |
| * |
| * GetEndpoints(IN yfsServerAttributes *attr, |
| * OUT opr_uuid *uuid, |
| * OUT afs_int32 *uniquifier, |
| * OUT endpoints *fsEndpoints, |
| * OUT endpoints *volEndpoints) |
| */ |
| static int afs_deliver_yfsvl_get_endpoints(struct afs_call *call) |
| { |
| struct afs_addr_list *alist; |
| __be32 *bp; |
| u32 uniquifier, size; |
| int ret; |
| |
| _enter("{%u,%zu,%u}", |
| call->unmarshall, iov_iter_count(call->_iter), call->count2); |
| |
| switch (call->unmarshall) { |
| case 0: |
| afs_extract_to_buf(call, sizeof(uuid_t) + 3 * sizeof(__be32)); |
| call->unmarshall = 1; |
| |
| /* Extract the returned uuid, uniquifier, fsEndpoints count and |
| * either the first fsEndpoint type or the volEndpoints |
| * count if there are no fsEndpoints. */ |
| case 1: |
| ret = afs_extract_data(call, true); |
| if (ret < 0) |
| return ret; |
| |
| bp = call->buffer + sizeof(uuid_t); |
| uniquifier = ntohl(*bp++); |
| call->count = ntohl(*bp++); |
| call->count2 = ntohl(*bp); /* Type or next count */ |
| |
| if (call->count > YFS_MAXENDPOINTS) |
| return afs_protocol_error(call, -EBADMSG, |
| afs_eproto_yvl_fsendpt_num); |
| |
| alist = afs_alloc_addrlist(call->count, FS_SERVICE, AFS_FS_PORT); |
| if (!alist) |
| return -ENOMEM; |
| alist->version = uniquifier; |
| call->reply[0] = alist; |
| |
| if (call->count == 0) |
| goto extract_volendpoints; |
| |
| next_fsendpoint: |
| switch (call->count2) { |
| case YFS_ENDPOINT_IPV4: |
| size = sizeof(__be32) * (1 + 1 + 1); |
| break; |
| case YFS_ENDPOINT_IPV6: |
| size = sizeof(__be32) * (1 + 4 + 1); |
| break; |
| default: |
| return afs_protocol_error(call, -EBADMSG, |
| afs_eproto_yvl_fsendpt_type); |
| } |
| |
| size += sizeof(__be32); |
| afs_extract_to_buf(call, size); |
| call->unmarshall = 2; |
| |
| /* Extract fsEndpoints[] entries */ |
| case 2: |
| ret = afs_extract_data(call, true); |
| if (ret < 0) |
| return ret; |
| |
| alist = call->reply[0]; |
| bp = call->buffer; |
| switch (call->count2) { |
| case YFS_ENDPOINT_IPV4: |
| if (ntohl(bp[0]) != sizeof(__be32) * 2) |
| return afs_protocol_error(call, -EBADMSG, |
| afs_eproto_yvl_fsendpt4_len); |
| afs_merge_fs_addr4(alist, bp[1], ntohl(bp[2])); |
| bp += 3; |
| break; |
| case YFS_ENDPOINT_IPV6: |
| if (ntohl(bp[0]) != sizeof(__be32) * 5) |
| return afs_protocol_error(call, -EBADMSG, |
| afs_eproto_yvl_fsendpt6_len); |
| afs_merge_fs_addr6(alist, bp + 1, ntohl(bp[5])); |
| bp += 6; |
| break; |
| default: |
| return afs_protocol_error(call, -EBADMSG, |
| afs_eproto_yvl_fsendpt_type); |
| } |
| |
| /* Got either the type of the next entry or the count of |
| * volEndpoints if no more fsEndpoints. |
| */ |
| call->count2 = ntohl(*bp++); |
| |
| call->count--; |
| if (call->count > 0) |
| goto next_fsendpoint; |
| |
| extract_volendpoints: |
| /* Extract the list of volEndpoints. */ |
| call->count = call->count2; |
| if (!call->count) |
| goto end; |
| if (call->count > YFS_MAXENDPOINTS) |
| return afs_protocol_error(call, -EBADMSG, |
| afs_eproto_yvl_vlendpt_type); |
| |
| afs_extract_to_buf(call, 1 * sizeof(__be32)); |
| call->unmarshall = 3; |
| |
| /* Extract the type of volEndpoints[0]. Normally we would |
| * extract the type of the next endpoint when we extract the |
| * data of the current one, but this is the first... |
| */ |
| case 3: |
| ret = afs_extract_data(call, true); |
| if (ret < 0) |
| return ret; |
| |
| bp = call->buffer; |
| |
| next_volendpoint: |
| call->count2 = ntohl(*bp++); |
| switch (call->count2) { |
| case YFS_ENDPOINT_IPV4: |
| size = sizeof(__be32) * (1 + 1 + 1); |
| break; |
| case YFS_ENDPOINT_IPV6: |
| size = sizeof(__be32) * (1 + 4 + 1); |
| break; |
| default: |
| return afs_protocol_error(call, -EBADMSG, |
| afs_eproto_yvl_vlendpt_type); |
| } |
| |
| if (call->count > 1) |
| size += sizeof(__be32); /* Get next type too */ |
| afs_extract_to_buf(call, size); |
| call->unmarshall = 4; |
| |
| /* Extract volEndpoints[] entries */ |
| case 4: |
| ret = afs_extract_data(call, true); |
| if (ret < 0) |
| return ret; |
| |
| bp = call->buffer; |
| switch (call->count2) { |
| case YFS_ENDPOINT_IPV4: |
| if (ntohl(bp[0]) != sizeof(__be32) * 2) |
| return afs_protocol_error(call, -EBADMSG, |
| afs_eproto_yvl_vlendpt4_len); |
| bp += 3; |
| break; |
| case YFS_ENDPOINT_IPV6: |
| if (ntohl(bp[0]) != sizeof(__be32) * 5) |
| return afs_protocol_error(call, -EBADMSG, |
| afs_eproto_yvl_vlendpt6_len); |
| bp += 6; |
| break; |
| default: |
| return afs_protocol_error(call, -EBADMSG, |
| afs_eproto_yvl_vlendpt_type); |
| } |
| |
| /* Got either the type of the next entry or the count of |
| * volEndpoints if no more fsEndpoints. |
| */ |
| call->count--; |
| if (call->count > 0) |
| goto next_volendpoint; |
| |
| end: |
| afs_extract_discard(call, 0); |
| call->unmarshall = 5; |
| |
| /* Done */ |
| case 5: |
| ret = afs_extract_data(call, false); |
| if (ret < 0) |
| return ret; |
| call->unmarshall = 6; |
| |
| case 6: |
| break; |
| } |
| |
| alist = call->reply[0]; |
| _leave(" = 0 [done]"); |
| return 0; |
| } |
| |
| /* |
| * YFSVL.GetEndpoints operation type. |
| */ |
| static const struct afs_call_type afs_YFSVLGetEndpoints = { |
| .name = "YFSVL.GetEndpoints", |
| .op = afs_YFSVL_GetEndpoints, |
| .deliver = afs_deliver_yfsvl_get_endpoints, |
| .destructor = afs_vl_get_addrs_u_destructor, |
| }; |
| |
| /* |
| * Dispatch an operation to get the addresses for a server, where the server is |
| * nominated by UUID. |
| */ |
| struct afs_addr_list *afs_yfsvl_get_endpoints(struct afs_vl_cursor *vc, |
| const uuid_t *uuid) |
| { |
| struct afs_call *call; |
| struct afs_net *net = vc->cell->net; |
| __be32 *bp; |
| |
| _enter(""); |
| |
| call = afs_alloc_flat_call(net, &afs_YFSVLGetEndpoints, |
| sizeof(__be32) * 2 + sizeof(*uuid), |
| sizeof(struct in6_addr) + sizeof(__be32) * 3); |
| if (!call) |
| return ERR_PTR(-ENOMEM); |
| |
| call->key = vc->key; |
| call->reply[0] = NULL; |
| call->ret_reply0 = true; |
| |
| /* Marshall the parameters */ |
| bp = call->request; |
| *bp++ = htonl(YVLGETENDPOINTS); |
| *bp++ = htonl(YFS_SERVER_UUID); |
| memcpy(bp, uuid, sizeof(*uuid)); /* Type opr_uuid */ |
| |
| trace_afs_make_vl_call(call); |
| return (struct afs_addr_list *)afs_make_call(&vc->ac, call, GFP_KERNEL, false); |
| } |