| |
| |
| |
| |
| |
| |
| |
| #include <linux/module.h> |
| #include <linux/init.h> |
| #include <linux/slab.h> |
| #include <linux/sched.h> |
| #include <linux/ip.h> |
| #include "internal.h" |
| #include "afs_cm.h" |
| #include "protocol_yfs.h" |
| |
| static int afs_deliver_cb_init_call_back_state(struct afs_call *); |
| static int afs_deliver_cb_init_call_back_state3(struct afs_call *); |
| static int afs_deliver_cb_probe(struct afs_call *); |
| static int afs_deliver_cb_callback(struct afs_call *); |
| static int afs_deliver_cb_probe_uuid(struct afs_call *); |
| static int afs_deliver_cb_tell_me_about_yourself(struct afs_call *); |
| static void afs_cm_destructor(struct afs_call *); |
| static void SRXAFSCB_CallBack(struct work_struct *); |
| static void SRXAFSCB_InitCallBackState(struct work_struct *); |
| static void SRXAFSCB_Probe(struct work_struct *); |
| static void SRXAFSCB_ProbeUuid(struct work_struct *); |
| static void SRXAFSCB_TellMeAboutYourself(struct work_struct *); |
| |
| static int afs_deliver_yfs_cb_callback(struct afs_call *); |
| |
| |
| |
| |
| static const struct afs_call_type afs_SRXCBCallBack = { |
| <------>.name = "CB.CallBack", |
| <------>.deliver = afs_deliver_cb_callback, |
| <------>.destructor = afs_cm_destructor, |
| <------>.work = SRXAFSCB_CallBack, |
| }; |
| |
| |
| |
| |
| static const struct afs_call_type afs_SRXCBInitCallBackState = { |
| <------>.name = "CB.InitCallBackState", |
| <------>.deliver = afs_deliver_cb_init_call_back_state, |
| <------>.destructor = afs_cm_destructor, |
| <------>.work = SRXAFSCB_InitCallBackState, |
| }; |
| |
| |
| |
| |
| static const struct afs_call_type afs_SRXCBInitCallBackState3 = { |
| <------>.name = "CB.InitCallBackState3", |
| <------>.deliver = afs_deliver_cb_init_call_back_state3, |
| <------>.destructor = afs_cm_destructor, |
| <------>.work = SRXAFSCB_InitCallBackState, |
| }; |
| |
| |
| |
| |
| static const struct afs_call_type afs_SRXCBProbe = { |
| <------>.name = "CB.Probe", |
| <------>.deliver = afs_deliver_cb_probe, |
| <------>.destructor = afs_cm_destructor, |
| <------>.work = SRXAFSCB_Probe, |
| }; |
| |
| |
| |
| |
| static const struct afs_call_type afs_SRXCBProbeUuid = { |
| <------>.name = "CB.ProbeUuid", |
| <------>.deliver = afs_deliver_cb_probe_uuid, |
| <------>.destructor = afs_cm_destructor, |
| <------>.work = SRXAFSCB_ProbeUuid, |
| }; |
| |
| |
| |
| |
| static const struct afs_call_type afs_SRXCBTellMeAboutYourself = { |
| <------>.name = "CB.TellMeAboutYourself", |
| <------>.deliver = afs_deliver_cb_tell_me_about_yourself, |
| <------>.destructor = afs_cm_destructor, |
| <------>.work = SRXAFSCB_TellMeAboutYourself, |
| }; |
| |
| |
| |
| |
| static const struct afs_call_type afs_SRXYFSCB_CallBack = { |
| <------>.name = "YFSCB.CallBack", |
| <------>.deliver = afs_deliver_yfs_cb_callback, |
| <------>.destructor = afs_cm_destructor, |
| <------>.work = SRXAFSCB_CallBack, |
| }; |
| |
| |
| |
| |
| |
| bool afs_cm_incoming_call(struct afs_call *call) |
| { |
| <------>_enter("{%u, CB.OP %u}", call->service_id, call->operation_ID); |
| |
| <------>switch (call->operation_ID) { |
| <------>case CBCallBack: |
| <------><------>call->type = &afs_SRXCBCallBack; |
| <------><------>return true; |
| <------>case CBInitCallBackState: |
| <------><------>call->type = &afs_SRXCBInitCallBackState; |
| <------><------>return true; |
| <------>case CBInitCallBackState3: |
| <------><------>call->type = &afs_SRXCBInitCallBackState3; |
| <------><------>return true; |
| <------>case CBProbe: |
| <------><------>call->type = &afs_SRXCBProbe; |
| <------><------>return true; |
| <------>case CBProbeUuid: |
| <------><------>call->type = &afs_SRXCBProbeUuid; |
| <------><------>return true; |
| <------>case CBTellMeAboutYourself: |
| <------><------>call->type = &afs_SRXCBTellMeAboutYourself; |
| <------><------>return true; |
| <------>case YFSCBCallBack: |
| <------><------>if (call->service_id != YFS_CM_SERVICE) |
| <------><------><------>return false; |
| <------><------>call->type = &afs_SRXYFSCB_CallBack; |
| <------><------>return true; |
| <------>default: |
| <------><------>return false; |
| <------>} |
| } |
| |
| |
| |
| |
| |
| static int afs_find_cm_server_by_peer(struct afs_call *call) |
| { |
| <------>struct sockaddr_rxrpc srx; |
| <------>struct afs_server *server; |
| |
| <------>rxrpc_kernel_get_peer(call->net->socket, call->rxcall, &srx); |
| |
| <------>server = afs_find_server(call->net, &srx); |
| <------>if (!server) { |
| <------><------>trace_afs_cm_no_server(call, &srx); |
| <------><------>return 0; |
| <------>} |
| |
| <------>call->server = server; |
| <------>return 0; |
| } |
| |
| |
| |
| |
| |
| static int afs_find_cm_server_by_uuid(struct afs_call *call, |
| <------><------><------><------> struct afs_uuid *uuid) |
| { |
| <------>struct afs_server *server; |
| |
| <------>rcu_read_lock(); |
| <------>server = afs_find_server_by_uuid(call->net, call->request); |
| <------>rcu_read_unlock(); |
| <------>if (!server) { |
| <------><------>trace_afs_cm_no_server_u(call, call->request); |
| <------><------>return 0; |
| <------>} |
| |
| <------>call->server = server; |
| <------>return 0; |
| } |
| |
| |
| |
| |
| static void afs_cm_destructor(struct afs_call *call) |
| { |
| <------>kfree(call->buffer); |
| <------>call->buffer = NULL; |
| } |
| |
| |
| |
| |
| static void afs_abort_service_call(struct afs_call *call, u32 abort_code, int error, |
| <------><------><------><------> const char *why) |
| { |
| <------>rxrpc_kernel_abort_call(call->net->socket, call->rxcall, |
| <------><------><------><------>abort_code, error, why); |
| <------>afs_set_call_complete(call, error, 0); |
| } |
| |
| |
| |
| |
| static void SRXAFSCB_CallBack(struct work_struct *work) |
| { |
| <------>struct afs_call *call = container_of(work, struct afs_call, work); |
| |
| <------>_enter(""); |
| |
| <------> |
| <------> * server holds up change visibility till it receives our reply so as |
| <------> * to maintain cache coherency. |
| <------> */ |
| <------>if (call->server) { |
| <------><------>trace_afs_server(call->server, |
| <------><------><------><------> atomic_read(&call->server->ref), |
| <------><------><------><------> atomic_read(&call->server->active), |
| <------><------><------><------> afs_server_trace_callback); |
| <------><------>afs_break_callbacks(call->server, call->count, call->request); |
| <------>} |
| |
| <------>afs_send_empty_reply(call); |
| <------>afs_put_call(call); |
| <------>_leave(""); |
| } |
| |
| |
| |
| |
| static int afs_deliver_cb_callback(struct afs_call *call) |
| { |
| <------>struct afs_callback_break *cb; |
| <------>__be32 *bp; |
| <------>int ret, loop; |
| |
| <------>_enter("{%u}", call->unmarshall); |
| |
| <------>switch (call->unmarshall) { |
| <------>case 0: |
| <------><------>afs_extract_to_tmp(call); |
| <------><------>call->unmarshall++; |
| |
| <------><------> |
| <------><------>fallthrough; |
| <------>case 1: |
| <------><------>_debug("extract FID count"); |
| <------><------>ret = afs_extract_data(call, true); |
| <------><------>if (ret < 0) |
| <------><------><------>return ret; |
| |
| <------><------>call->count = ntohl(call->tmp); |
| <------><------>_debug("FID count: %u", call->count); |
| <------><------>if (call->count > AFSCBMAX) |
| <------><------><------>return afs_protocol_error(call, afs_eproto_cb_fid_count); |
| |
| <------><------>call->buffer = kmalloc(array3_size(call->count, 3, 4), |
| <------><------><------><------> GFP_KERNEL); |
| <------><------>if (!call->buffer) |
| <------><------><------>return -ENOMEM; |
| <------><------>afs_extract_to_buf(call, call->count * 3 * 4); |
| <------><------>call->unmarshall++; |
| |
| <------><------>fallthrough; |
| <------>case 2: |
| <------><------>_debug("extract FID array"); |
| <------><------>ret = afs_extract_data(call, true); |
| <------><------>if (ret < 0) |
| <------><------><------>return ret; |
| |
| <------><------>_debug("unmarshall FID array"); |
| <------><------>call->request = kcalloc(call->count, |
| <------><------><------><------><------>sizeof(struct afs_callback_break), |
| <------><------><------><------><------>GFP_KERNEL); |
| <------><------>if (!call->request) |
| <------><------><------>return -ENOMEM; |
| |
| <------><------>cb = call->request; |
| <------><------>bp = call->buffer; |
| <------><------>for (loop = call->count; loop > 0; loop--, cb++) { |
| <------><------><------>cb->fid.vid = ntohl(*bp++); |
| <------><------><------>cb->fid.vnode = ntohl(*bp++); |
| <------><------><------>cb->fid.unique = ntohl(*bp++); |
| <------><------>} |
| |
| <------><------>afs_extract_to_tmp(call); |
| <------><------>call->unmarshall++; |
| |
| <------><------> |
| <------><------>fallthrough; |
| <------>case 3: |
| <------><------>_debug("extract CB count"); |
| <------><------>ret = afs_extract_data(call, true); |
| <------><------>if (ret < 0) |
| <------><------><------>return ret; |
| |
| <------><------>call->count2 = ntohl(call->tmp); |
| <------><------>_debug("CB count: %u", call->count2); |
| <------><------>if (call->count2 != call->count && call->count2 != 0) |
| <------><------><------>return afs_protocol_error(call, afs_eproto_cb_count); |
| <------><------>call->iter = &call->def_iter; |
| <------><------>iov_iter_discard(&call->def_iter, READ, call->count2 * 3 * 4); |
| <------><------>call->unmarshall++; |
| |
| <------><------>fallthrough; |
| <------>case 4: |
| <------><------>_debug("extract discard %zu/%u", |
| <------><------> iov_iter_count(call->iter), call->count2 * 3 * 4); |
| |
| <------><------>ret = afs_extract_data(call, false); |
| <------><------>if (ret < 0) |
| <------><------><------>return ret; |
| |
| <------><------>call->unmarshall++; |
| <------>case 5: |
| <------><------>break; |
| <------>} |
| |
| <------>if (!afs_check_call_state(call, AFS_CALL_SV_REPLYING)) |
| <------><------>return afs_io_error(call, afs_io_error_cm_reply); |
| |
| <------> |
| <------> * vnodes to operate upon */ |
| <------>return afs_find_cm_server_by_peer(call); |
| } |
| |
| |
| |
| |
| static void SRXAFSCB_InitCallBackState(struct work_struct *work) |
| { |
| <------>struct afs_call *call = container_of(work, struct afs_call, work); |
| |
| <------>_enter("{%p}", call->server); |
| |
| <------>if (call->server) |
| <------><------>afs_init_callback_state(call->server); |
| <------>afs_send_empty_reply(call); |
| <------>afs_put_call(call); |
| <------>_leave(""); |
| } |
| |
| |
| |
| |
| static int afs_deliver_cb_init_call_back_state(struct afs_call *call) |
| { |
| <------>int ret; |
| |
| <------>_enter(""); |
| |
| <------>afs_extract_discard(call, 0); |
| <------>ret = afs_extract_data(call, false); |
| <------>if (ret < 0) |
| <------><------>return ret; |
| |
| <------> |
| <------> * vnodes to operate upon */ |
| <------>return afs_find_cm_server_by_peer(call); |
| } |
| |
| |
| |
| |
| static int afs_deliver_cb_init_call_back_state3(struct afs_call *call) |
| { |
| <------>struct afs_uuid *r; |
| <------>unsigned loop; |
| <------>__be32 *b; |
| <------>int ret; |
| |
| <------>_enter(""); |
| |
| <------>_enter("{%u}", call->unmarshall); |
| |
| <------>switch (call->unmarshall) { |
| <------>case 0: |
| <------><------>call->buffer = kmalloc_array(11, sizeof(__be32), GFP_KERNEL); |
| <------><------>if (!call->buffer) |
| <------><------><------>return -ENOMEM; |
| <------><------>afs_extract_to_buf(call, 11 * sizeof(__be32)); |
| <------><------>call->unmarshall++; |
| |
| <------><------>fallthrough; |
| <------>case 1: |
| <------><------>_debug("extract UUID"); |
| <------><------>ret = afs_extract_data(call, false); |
| <------><------>switch (ret) { |
| <------><------>case 0: break; |
| <------><------>case -EAGAIN: return 0; |
| <------><------>default: return ret; |
| <------><------>} |
| |
| <------><------>_debug("unmarshall UUID"); |
| <------><------>call->request = kmalloc(sizeof(struct afs_uuid), GFP_KERNEL); |
| <------><------>if (!call->request) |
| <------><------><------>return -ENOMEM; |
| |
| <------><------>b = call->buffer; |
| <------><------>r = call->request; |
| <------><------>r->time_low = b[0]; |
| <------><------>r->time_mid = htons(ntohl(b[1])); |
| <------><------>r->time_hi_and_version = htons(ntohl(b[2])); |
| <------><------>r->clock_seq_hi_and_reserved = ntohl(b[3]); |
| <------><------>r->clock_seq_low = ntohl(b[4]); |
| |
| <------><------>for (loop = 0; loop < 6; loop++) |
| <------><------><------>r->node[loop] = ntohl(b[loop + 5]); |
| |
| <------><------>call->unmarshall++; |
| |
| <------>case 2: |
| <------><------>break; |
| <------>} |
| |
| <------>if (!afs_check_call_state(call, AFS_CALL_SV_REPLYING)) |
| <------><------>return afs_io_error(call, afs_io_error_cm_reply); |
| |
| <------> |
| <------> * vnodes to operate upon */ |
| <------>return afs_find_cm_server_by_uuid(call, call->request); |
| } |
| |
| |
| |
| |
| static void SRXAFSCB_Probe(struct work_struct *work) |
| { |
| <------>struct afs_call *call = container_of(work, struct afs_call, work); |
| |
| <------>_enter(""); |
| <------>afs_send_empty_reply(call); |
| <------>afs_put_call(call); |
| <------>_leave(""); |
| } |
| |
| |
| |
| |
| static int afs_deliver_cb_probe(struct afs_call *call) |
| { |
| <------>int ret; |
| |
| <------>_enter(""); |
| |
| <------>afs_extract_discard(call, 0); |
| <------>ret = afs_extract_data(call, false); |
| <------>if (ret < 0) |
| <------><------>return ret; |
| |
| <------>if (!afs_check_call_state(call, AFS_CALL_SV_REPLYING)) |
| <------><------>return afs_io_error(call, afs_io_error_cm_reply); |
| <------>return afs_find_cm_server_by_peer(call); |
| } |
| |
| |
| |
| |
| |
| static void SRXAFSCB_ProbeUuid(struct work_struct *work) |
| { |
| <------>struct afs_call *call = container_of(work, struct afs_call, work); |
| <------>struct afs_uuid *r = call->request; |
| |
| <------>_enter(""); |
| |
| <------>if (memcmp(r, &call->net->uuid, sizeof(call->net->uuid)) == 0) |
| <------><------>afs_send_empty_reply(call); |
| <------>else |
| <------><------>afs_abort_service_call(call, 1, 1, "K-1"); |
| |
| <------>afs_put_call(call); |
| <------>_leave(""); |
| } |
| |
| |
| |
| |
| static int afs_deliver_cb_probe_uuid(struct afs_call *call) |
| { |
| <------>struct afs_uuid *r; |
| <------>unsigned loop; |
| <------>__be32 *b; |
| <------>int ret; |
| |
| <------>_enter("{%u}", call->unmarshall); |
| |
| <------>switch (call->unmarshall) { |
| <------>case 0: |
| <------><------>call->buffer = kmalloc_array(11, sizeof(__be32), GFP_KERNEL); |
| <------><------>if (!call->buffer) |
| <------><------><------>return -ENOMEM; |
| <------><------>afs_extract_to_buf(call, 11 * sizeof(__be32)); |
| <------><------>call->unmarshall++; |
| |
| <------><------>fallthrough; |
| <------>case 1: |
| <------><------>_debug("extract UUID"); |
| <------><------>ret = afs_extract_data(call, false); |
| <------><------>switch (ret) { |
| <------><------>case 0: break; |
| <------><------>case -EAGAIN: return 0; |
| <------><------>default: return ret; |
| <------><------>} |
| |
| <------><------>_debug("unmarshall UUID"); |
| <------><------>call->request = kmalloc(sizeof(struct afs_uuid), GFP_KERNEL); |
| <------><------>if (!call->request) |
| <------><------><------>return -ENOMEM; |
| |
| <------><------>b = call->buffer; |
| <------><------>r = call->request; |
| <------><------>r->time_low = b[0]; |
| <------><------>r->time_mid = htons(ntohl(b[1])); |
| <------><------>r->time_hi_and_version = htons(ntohl(b[2])); |
| <------><------>r->clock_seq_hi_and_reserved = ntohl(b[3]); |
| <------><------>r->clock_seq_low = ntohl(b[4]); |
| |
| <------><------>for (loop = 0; loop < 6; loop++) |
| <------><------><------>r->node[loop] = ntohl(b[loop + 5]); |
| |
| <------><------>call->unmarshall++; |
| |
| <------>case 2: |
| <------><------>break; |
| <------>} |
| |
| <------>if (!afs_check_call_state(call, AFS_CALL_SV_REPLYING)) |
| <------><------>return afs_io_error(call, afs_io_error_cm_reply); |
| <------>return afs_find_cm_server_by_peer(call); |
| } |
| |
| |
| |
| |
| static void SRXAFSCB_TellMeAboutYourself(struct work_struct *work) |
| { |
| <------>struct afs_call *call = container_of(work, struct afs_call, work); |
| <------>int loop; |
| |
| <------>struct { |
| <------><------>struct /* InterfaceAddr */ { |
| <------><------><------>__be32 nifs; |
| <------><------><------>__be32 uuid[11]; |
| <------><------><------>__be32 ifaddr[32]; |
| <------><------><------>__be32 netmask[32]; |
| <------><------><------>__be32 mtu[32]; |
| <------><------>} ia; |
| <------><------>struct /* Capabilities */ { |
| <------><------><------>__be32 capcount; |
| <------><------><------>__be32 caps[1]; |
| <------><------>} cap; |
| <------>} reply; |
| |
| <------>_enter(""); |
| |
| <------>memset(&reply, 0, sizeof(reply)); |
| |
| <------>reply.ia.uuid[0] = call->net->uuid.time_low; |
| <------>reply.ia.uuid[1] = htonl(ntohs(call->net->uuid.time_mid)); |
| <------>reply.ia.uuid[2] = htonl(ntohs(call->net->uuid.time_hi_and_version)); |
| <------>reply.ia.uuid[3] = htonl((s8) call->net->uuid.clock_seq_hi_and_reserved); |
| <------>reply.ia.uuid[4] = htonl((s8) call->net->uuid.clock_seq_low); |
| <------>for (loop = 0; loop < 6; loop++) |
| <------><------>reply.ia.uuid[loop + 5] = htonl((s8) call->net->uuid.node[loop]); |
| |
| <------>reply.cap.capcount = htonl(1); |
| <------>reply.cap.caps[0] = htonl(AFS_CAP_ERROR_TRANSLATION); |
| <------>afs_send_simple_reply(call, &reply, sizeof(reply)); |
| <------>afs_put_call(call); |
| <------>_leave(""); |
| } |
| |
| |
| |
| |
| static int afs_deliver_cb_tell_me_about_yourself(struct afs_call *call) |
| { |
| <------>int ret; |
| |
| <------>_enter(""); |
| |
| <------>afs_extract_discard(call, 0); |
| <------>ret = afs_extract_data(call, false); |
| <------>if (ret < 0) |
| <------><------>return ret; |
| |
| <------>if (!afs_check_call_state(call, AFS_CALL_SV_REPLYING)) |
| <------><------>return afs_io_error(call, afs_io_error_cm_reply); |
| <------>return afs_find_cm_server_by_peer(call); |
| } |
| |
| |
| |
| |
| static int afs_deliver_yfs_cb_callback(struct afs_call *call) |
| { |
| <------>struct afs_callback_break *cb; |
| <------>struct yfs_xdr_YFSFid *bp; |
| <------>size_t size; |
| <------>int ret, loop; |
| |
| <------>_enter("{%u}", call->unmarshall); |
| |
| <------>switch (call->unmarshall) { |
| <------>case 0: |
| <------><------>afs_extract_to_tmp(call); |
| <------><------>call->unmarshall++; |
| |
| <------><------> |
| <------><------>fallthrough; |
| <------>case 1: |
| <------><------>_debug("extract FID count"); |
| <------><------>ret = afs_extract_data(call, true); |
| <------><------>if (ret < 0) |
| <------><------><------>return ret; |
| |
| <------><------>call->count = ntohl(call->tmp); |
| <------><------>_debug("FID count: %u", call->count); |
| <------><------>if (call->count > YFSCBMAX) |
| <------><------><------>return afs_protocol_error(call, afs_eproto_cb_fid_count); |
| |
| <------><------>size = array_size(call->count, sizeof(struct yfs_xdr_YFSFid)); |
| <------><------>call->buffer = kmalloc(size, GFP_KERNEL); |
| <------><------>if (!call->buffer) |
| <------><------><------>return -ENOMEM; |
| <------><------>afs_extract_to_buf(call, size); |
| <------><------>call->unmarshall++; |
| |
| <------><------>fallthrough; |
| <------>case 2: |
| <------><------>_debug("extract FID array"); |
| <------><------>ret = afs_extract_data(call, false); |
| <------><------>if (ret < 0) |
| <------><------><------>return ret; |
| |
| <------><------>_debug("unmarshall FID array"); |
| <------><------>call->request = kcalloc(call->count, |
| <------><------><------><------><------>sizeof(struct afs_callback_break), |
| <------><------><------><------><------>GFP_KERNEL); |
| <------><------>if (!call->request) |
| <------><------><------>return -ENOMEM; |
| |
| <------><------>cb = call->request; |
| <------><------>bp = call->buffer; |
| <------><------>for (loop = call->count; loop > 0; loop--, cb++) { |
| <------><------><------>cb->fid.vid = xdr_to_u64(bp->volume); |
| <------><------><------>cb->fid.vnode = xdr_to_u64(bp->vnode.lo); |
| <------><------><------>cb->fid.vnode_hi = ntohl(bp->vnode.hi); |
| <------><------><------>cb->fid.unique = ntohl(bp->vnode.unique); |
| <------><------><------>bp++; |
| <------><------>} |
| |
| <------><------>afs_extract_to_tmp(call); |
| <------><------>call->unmarshall++; |
| |
| <------>case 3: |
| <------><------>break; |
| <------>} |
| |
| <------>if (!afs_check_call_state(call, AFS_CALL_SV_REPLYING)) |
| <------><------>return afs_io_error(call, afs_io_error_cm_reply); |
| |
| <------> |
| <------> * vnodes to operate upon. |
| <------> */ |
| <------>return afs_find_cm_server_by_peer(call); |
| } |
| |