1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 #include "config.h"
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <sys/types.h>
7 #include <inttypes.h>
8 
9 #include "extensions/protocol_extension.h"
10 #include <memcached/util.h>
11 #include <platform/platform.h>
12 #include "fragment_rw.h"
13 
14 static uint8_t read_command = PROTOCOL_BINARY_CMD_READ;
15 static uint8_t write_command = PROTOCOL_BINARY_CMD_WRITE;
16 
17 GET_SERVER_API server_api;
18 
19 static const char *get_name(void);
20 static void setup(void (*add)(EXTENSION_BINARY_PROTOCOL_DESCRIPTOR *descriptor,
21                               uint8_t cmd,
22                               BINARY_COMMAND_CALLBACK new_handler));
23 
24 static ENGINE_ERROR_CODE handle_fragment_rw(EXTENSION_BINARY_PROTOCOL_DESCRIPTOR *descriptor,
25                                             ENGINE_HANDLE* handle,
26                                             const void* cookie,
27                                             protocol_binary_request_header *request,
28                                             ADD_RESPONSE response);
29 
30 static EXTENSION_BINARY_PROTOCOL_DESCRIPTOR descriptor;
31 
get_name(void)32 static const char *get_name(void) {
33     return "fragment read/write";
34 }
35 
setup(void (*add)(EXTENSION_BINARY_PROTOCOL_DESCRIPTOR *descriptor, uint8_t cmd, BINARY_COMMAND_CALLBACK new_handler))36 static void setup(void (*add)(EXTENSION_BINARY_PROTOCOL_DESCRIPTOR *descriptor,
37                               uint8_t cmd,
38                               BINARY_COMMAND_CALLBACK new_handler))
39 {
40     add(&descriptor, read_command, handle_fragment_rw);
41     add(&descriptor, write_command, handle_fragment_rw);
42 }
43 
create_object(ENGINE_HANDLE_V1 *v1, ENGINE_HANDLE *v, const void *cookie, const item_info *org, uint16_t vbucket, const void *data, uint64_t offset, uint64_t len, uint64_t *cas, uint8_t datatype)44 static ENGINE_ERROR_CODE create_object(ENGINE_HANDLE_V1 *v1,
45                                        ENGINE_HANDLE *v,
46                                        const void *cookie,
47                                        const item_info *org,
48                                        uint16_t vbucket,
49                                        const void *data,
50                                        uint64_t offset,
51                                        uint64_t len,
52                                        uint64_t *cas,
53                                        uint8_t datatype)
54 {
55     ENGINE_ERROR_CODE r;
56     item *item = NULL;
57     item_info i2;
58     uint8_t *dest;
59 
60     r = v1->allocate(v, cookie, &item, org->key, org->nkey, org->nbytes,
61                      org->flags, vbucket, datatype);
62     if (r != ENGINE_SUCCESS) {
63         return r;
64     }
65 
66     i2.nvalue = 1;
67 
68     if (!v1->get_item_info(v, cookie, item, &i2)) {
69         v1->release(v, cookie, item);
70         return ENGINE_DISCONNECT;
71     }
72 
73     dest = (void*)i2.value[0].iov_base;
74     memcpy(dest, org->value[0].iov_base, org->nbytes);
75     memcpy(dest + offset, data, len);
76 
77     v1->item_set_cas(v, cookie, item, org->cas);
78     r = v1->store(v, cookie, item, cas, OPERATION_CAS, vbucket);
79     v1->release(v, cookie, item);
80     return r;
81 }
82 
handle_fragment_rw(EXTENSION_BINARY_PROTOCOL_DESCRIPTOR *descriptor, ENGINE_HANDLE* handle, const void* cookie, protocol_binary_request_header *request, ADD_RESPONSE response)83 static ENGINE_ERROR_CODE handle_fragment_rw(EXTENSION_BINARY_PROTOCOL_DESCRIPTOR *descriptor,
84                                             ENGINE_HANDLE* handle,
85                                             const void* cookie,
86                                             protocol_binary_request_header *request,
87                                             ADD_RESPONSE response)
88 {
89     protocol_binary_request_read *req;
90     uint8_t *key;
91     uint16_t nkey;
92     uint64_t offset;
93     uint64_t len;
94     uint16_t vbucket;
95     uint64_t cas;
96     ENGINE_HANDLE_V1 *v1;
97     item *item;
98     uint8_t *data;
99     ENGINE_ERROR_CODE r;
100 
101     if (request->request.extlen != 8 || request->request.keylen == 0) {
102         return response(NULL, 0, NULL, 0, NULL, 0, PROTOCOL_BINARY_RAW_BYTES,
103                         PROTOCOL_BINARY_RESPONSE_EINVAL, 0, cookie);
104     }
105 
106     req = (void*)request;
107     key = req->bytes + sizeof(request->bytes) + request->request.extlen;
108     nkey = ntohs(request->request.keylen);
109     offset = ntohl(req->message.body.offset);
110     len = ntohl(req->message.body.length);
111     vbucket = ntohs(request->request.vbucket);
112     cas = ntohll(request->request.cas);
113     v1 = (void*)handle;
114     item = NULL;
115     data = key + nkey;
116 
117     r = v1->get(handle, cookie, &item, key, nkey, vbucket);
118     if (r == ENGINE_SUCCESS) {
119         item_info item_info;
120         item_info.nvalue = 1;
121 
122         if (!v1->get_item_info(handle, NULL, item, &item_info)) {
123             r = ENGINE_FAILED;
124         } else if (cas != 0 && item_info.cas != cas) {
125             r = ENGINE_KEY_EEXISTS;
126         } else if (offset + len > (uint64_t)item_info.nbytes) {
127             r = ENGINE_ERANGE;
128         }
129 
130         if (r == ENGINE_SUCCESS) {
131             if (request->request.opcode == read_command) {
132                 uint8_t *ptr;
133                 ptr =  ((uint8_t*)item_info.value[0].iov_base) + offset;
134                 if (!response(NULL, 0, NULL, 0, ptr,
135                               (uint32_t)len, PROTOCOL_BINARY_RAW_BYTES,
136                               PROTOCOL_BINARY_RESPONSE_SUCCESS,
137                               item_info.cas, cookie)) {
138                     return ENGINE_DISCONNECT;
139                 }
140             } else {
141                 r = create_object(v1, handle, cookie, &item_info,
142                                   vbucket, data, offset, len, &cas,
143                                   request->request.datatype);
144                 if (r == ENGINE_SUCCESS) {
145                     if (!response(NULL, 0, NULL, 0, NULL, 0,
146                                   PROTOCOL_BINARY_RAW_BYTES,
147                                   PROTOCOL_BINARY_RESPONSE_SUCCESS,
148                                   cas, cookie)) {
149                         return ENGINE_DISCONNECT;
150                     }
151                 }
152             }
153         }
154 
155         v1->release(handle, cookie, item);
156     }
157 
158     return r;
159 }
160 
161 MEMCACHED_PUBLIC_API
memcached_extensions_initialize(const char *config, GET_SERVER_API get_server_api)162 EXTENSION_ERROR_CODE memcached_extensions_initialize(const char *config,
163                                                      GET_SERVER_API get_server_api) {
164     SERVER_HANDLE_V1 *server = get_server_api();
165     server_api = get_server_api;
166     descriptor.get_name = get_name;
167     descriptor.setup = setup;
168 
169     if (server == NULL) {
170         return EXTENSION_FATAL;
171     }
172 
173     if (config != NULL) {
174         size_t rop, wop;
175         struct config_item items[3];
176         memset(&items, 0, sizeof(items));
177         items[0].key = "r";
178         items[0].datatype = DT_SIZE;
179         items[0].value.dt_size = &rop;
180         items[1].key = "w";
181         items[1].datatype = DT_SIZE;
182         items[1].value.dt_size = &wop;
183         items[2].key = NULL;
184 
185         if (server->core->parse_config(config, items, stderr) != 0) {
186             return EXTENSION_FATAL;
187         }
188 
189         if (items[0].found) {
190             read_command = (uint8_t)(rop & 0xff);
191         }
192 
193         if (items[1].found) {
194             write_command = (uint8_t)(wop & 0xff);
195         }
196     }
197 
198     if (!server->extension->register_extension(EXTENSION_BINARY_PROTOCOL,
199                                                &descriptor)) {
200         return EXTENSION_FATAL;
201     }
202 
203     return EXTENSION_SUCCESS;
204 }
205