s3: Fix reading beyond the end of a named stream in xattr_streams
[ira/wip.git] / source3 / modules / perfcount_onefs.c
1 /*
2  * Unix SMB/CIFS implementation.
3  * Support for OneFS protocol statistics / perfcounters
4  *
5  * Copyright (C) Todd Stecher 2008
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include "includes.h"
22 #include <sys/isi_stats_protocol.h>
23 #include <sys/isi_stats_client.h>
24 #include <sys/isi_stats_cifs.h>
25
26 extern struct current_user current_user;
27
28 struct onefs_op_counter {
29         struct isp_op_delta iod;
30         struct onefs_op_counter *next;
31         struct onefs_op_counter *prev;
32 };
33
34 struct onefs_stats_context {
35         bool alloced;
36         struct isp_op_delta iod;
37
38         /* ANDX commands stats stored here */
39         struct onefs_op_counter *ops_chain;
40 };
41
42 const char *onefs_stat_debug(struct isp_op_delta *iod);
43
44 struct onefs_stats_context g_context;
45
46 static void onefs_smb_statistics_end(struct smb_perfcount_data *pcd);
47
48 struct isp_op_delta *onefs_stats_get_op_delta(struct onefs_stats_context *ctxt)
49 {
50         /* operate on head of chain */
51         if (ctxt->ops_chain) {
52 #ifdef ONEFS_PERF_DEBUG
53                 DEBUG(0,("************* CHAINED *****\n"));
54 #endif
55                 return &ctxt->ops_chain->iod;
56         } else
57                 return &ctxt->iod;
58 }
59
60 /* statistics operations */
61 static void onefs_smb_statistics_start(struct smb_perfcount_data *pcd)
62 {
63
64 #ifdef ONEFS_PERF_DEBUG
65         if (g_context.iod.op) {
66                 DEBUG(0,("**************** OP Collision! %s(%d) \n",
67                         onefs_stat_debug(&g_context.iod), g_context.iod.op));
68         }
69
70 #endif
71
72         ISP_OP_BEG(&g_context.iod, ISP_PROTO_CIFS, 0);
73
74         if (g_context.iod.enabled)
75                 pcd->context = &g_context;
76         else
77                 pcd->context = NULL;
78
79
80 }
81
82 static void onefs_smb_statistics_add(struct smb_perfcount_data *pcd)
83 {
84         struct onefs_op_counter *oc;
85         struct onefs_stats_context *ctxt = pcd->context;
86
87         /* not enabled */
88         if (pcd->context == NULL)
89                 return;
90
91         oc = SMB_MALLOC_P(struct onefs_op_counter);
92
93         if (oc == NULL)
94                 return;
95
96 #ifdef ONEFS_PERF_DEBUG
97         DEBUG(0,("*********** add chained op \n"));
98 #endif
99
100         DLIST_ADD(ctxt->ops_chain, oc);
101         ISP_OP_BEG(&oc->iod, ISP_PROTO_CIFS, 0);
102 }
103
104 static void onefs_smb_statistics_set_op(struct smb_perfcount_data *pcd, int op)
105 {
106         struct onefs_stats_context *ctxt = pcd->context;
107         struct isp_op_delta *iod;
108
109         /* not enabled */
110         if (pcd->context == NULL)
111                 return;
112
113         iod = onefs_stats_get_op_delta(ctxt);
114         iod->op = isp_cifs_op_id(op);
115
116 #ifdef ONEFS_PERF_DEBUG
117         DEBUG(0,("***********SET op %s(%d)\n", onefs_stat_debug(iod), op));
118 #endif
119         /* no reply required */
120         if (op == SMBntcancel)
121                 onefs_smb_statistics_end(pcd);
122
123 }
124
125 static void onefs_smb_statistics_set_subop(struct smb_perfcount_data *pcd,
126                                            int subop)
127 {
128         struct onefs_stats_context *ctxt = pcd->context;
129         struct isp_op_delta *iod;
130
131         /* not enabled */
132         if (pcd->context == NULL)
133                 return;
134
135         iod = onefs_stats_get_op_delta(ctxt);
136         iod->op = isp_cifs_sub_op_id(iod->op, subop);
137
138 #ifdef ONEFS_PERF_DEBUG
139         DEBUG(0,("********************  SET subop %s(%d)\n",
140                 onefs_stat_debug(iod), subop));
141 #endif
142
143         /*
144          * finalize this one - we don't need to know when it
145          * is set, but its useful as a counter
146          */
147         if (subop == NT_TRANSACT_NOTIFY_CHANGE)
148                 onefs_smb_statistics_end(pcd);
149 }
150
151 static void onefs_smb_statistics_set_ioctl(struct smb_perfcount_data *pcd,
152                                            int io_ctl)
153 {
154         struct onefs_stats_context *ctxt = pcd->context;
155         struct isp_op_delta *iod;
156
157         /* not enabled */
158         if (pcd->context == NULL)
159                 return;
160
161         /* we only monitor shadow copy */
162         if (io_ctl != FSCTL_GET_SHADOW_COPY_DATA)
163                 return;
164
165         iod = onefs_stats_get_op_delta(ctxt);
166         iod->op = isp_cifs_sub_op_id(iod->op, ISP_CIFS_NTRN_IOCTL_FGSCD);
167 #ifdef ONEFS_PERF_DEBUG
168         DEBUG(0,("********************  SET ioctl %s(%d)\n",
169                 onefs_stat_debug(iod), io_ctl));
170 #endif
171 }
172
173 static void onefs_smb_statistics_set_msglen_in(struct smb_perfcount_data *pcd,
174                                                uint64_t in_bytes)
175 {
176         struct onefs_stats_context *ctxt = pcd->context;
177         struct isp_op_delta *iod;
178
179         /* not enabled */
180         if (pcd->context == NULL)
181                 return;
182
183         iod = onefs_stats_get_op_delta(ctxt);
184         iod->in_bytes = in_bytes;
185 }
186
187 static void onefs_smb_statistics_set_msglen_out(struct smb_perfcount_data *pcd,
188                                                 uint64_t out_bytes)
189 {
190         struct onefs_stats_context *ctxt = pcd->context;
191
192         /* not enabled */
193         if (pcd->context == NULL)
194                 return;
195
196         if (ctxt->ops_chain)
197                 ctxt->ops_chain->iod.out_bytes = out_bytes;
198
199         ctxt->iod.out_bytes = out_bytes;
200 }
201
202 static int onefs_copy_perfcount_context(struct onefs_stats_context *ctxt,
203                                         struct onefs_stats_context **dest)
204 {
205         struct onefs_stats_context *new_ctxt;
206
207         /* make an alloc'd copy of the data */
208         new_ctxt = SMB_MALLOC_P(struct onefs_stats_context);
209         if (!new_ctxt) {
210                 return -1;
211         }
212
213         memcpy(new_ctxt, ctxt, sizeof(struct onefs_stats_context));
214         new_ctxt->alloced = True;
215         *dest = new_ctxt;
216         return 0;
217 }
218
219 static void onefs_smb_statistics_copy_context(struct smb_perfcount_data *pcd,
220                                               struct smb_perfcount_data *dest)
221 {
222         struct onefs_stats_context *ctxt = pcd->context;
223         struct onefs_stats_context *new_ctxt;
224         int ret;
225
226         /* not enabled */
227         if (pcd->context == NULL)
228                 return;
229
230 #ifdef ONEFS_PERF_DEBUG
231         DEBUG(0,("********  COPYING op %s(%d)\n",
232                 onefs_stat_debug(&ctxt->iod), ctxt->iod.op));
233 #endif
234
235         ret = onefs_copy_perfcount_context(ctxt, &new_ctxt);
236         if (ret)
237                 return;
238
239         /* instrumentation */
240         if (ctxt == &g_context)
241                 ZERO_STRUCT(g_context);
242
243         dest->context = new_ctxt;
244 }
245
246 /*
247  * For perf reasons, we usually use the global - sometimes, though,
248  * when an operation is deferred, we need to alloc a copy.
249  */
250 static void onefs_smb_statistics_defer_op(struct smb_perfcount_data *pcd,
251                                           struct smb_perfcount_data *def_pcd)
252 {
253         struct onefs_stats_context *ctxt = pcd->context;
254         struct onefs_stats_context *deferred_ctxt;
255         int ret;
256
257         /* not enabled */
258         if (pcd->context == NULL)
259                 return;
260
261         /* already allocated ? */
262         if (ctxt->alloced)
263         {
264                 def_pcd->context = ctxt;
265                 pcd->context = NULL;
266                 return;
267         }
268
269 #ifdef ONEFS_PERF_DEBUG
270         DEBUG(0,("********  DEFERRING op %s(%d)\n",
271                 onefs_stat_debug(&ctxt->iod), ctxt->iod.op));
272 #endif
273
274         ret = onefs_copy_perfcount_context(ctxt, &deferred_ctxt);
275         if (ret)
276                 return;
277
278         def_pcd->context = (void*) deferred_ctxt;
279
280         /* instrumentation */
281         if (ctxt == &g_context)
282                 ZERO_STRUCT(g_context);
283
284         if (pcd != def_pcd)
285                 pcd->context = NULL;
286 }
287
288 static void onefs_smb_statistics_end(struct smb_perfcount_data *pcd)
289 {
290         struct onefs_stats_context *ctxt = pcd->context;
291         struct onefs_op_counter *tmp;
292         uint64_t uid;
293
294         static in_addr_t rem_addr = 0;
295         static in_addr_t loc_addr = 0;
296
297         /* not enabled */
298         if (pcd->context == NULL)
299                 return;
300
301         uid = current_user.ut.uid ? current_user.ut.uid : ISC_UNKNOWN_CLIENT_ID;
302
303         /* get address info once, doesn't change for process */
304         if (rem_addr == 0) {
305                 struct sockaddr_storage sa;
306                 socklen_t sa_len;
307                 int fd = smbd_server_fd();
308
309                 sa_len = sizeof sa;
310                 if (getpeername(fd, (struct sockaddr *)&sa, &sa_len) == 0 && 
311                     sa.ss_family == AF_INET)
312                         rem_addr = ((struct sockaddr_in *)&sa)->sin_addr.s_addr;
313                 else
314                         rem_addr = ISC_MASKED_ADDR;
315
316                 sa_len = sizeof sa;
317                 if (getsockname(fd, (struct sockaddr *)&sa, &sa_len) == 0 &&
318                     sa.ss_family == AF_INET)
319                         loc_addr = ((struct sockaddr_in *)&sa)->sin_addr.s_addr;
320                 else
321                         loc_addr = ISC_MASKED_ADDR;
322         }
323
324         /*
325          * bug here - we aren't getting the outlens right,
326          * when dealing w/ chained requests.
327          */
328         for (tmp = ctxt->ops_chain; tmp; tmp = tmp->next) {
329                 tmp->iod.out_bytes = ctxt->iod.out_bytes;
330                 isc_cookie_init(&tmp->iod.cookie, rem_addr, loc_addr, uid);
331                 ISP_OP_END(&tmp->iod);
332 #ifdef ONEFS_PERF_DEBUG
333                 DEBUG(0,("********  Finalized CHAIN op %s uid %llu in:%llu"
334                         ", out:%llu\n",
335                         onefs_stat_debug(&tmp->iod), uid,
336                         tmp->iod.in_bytes, tmp->iod.out_bytes));
337 #endif
338                 SAFE_FREE(tmp->prev);
339         }
340
341         isc_cookie_init(&ctxt->iod.cookie, rem_addr, loc_addr, uid);
342         ISP_OP_END(&ctxt->iod);
343 #ifdef ONEFS_PERF_DEBUG
344         DEBUG(0,("********  Finalized op %s uid %llu in:%llu, out:%llu\n",
345                 onefs_stat_debug(&ctxt->iod), uid,
346                 ctxt->iod.in_bytes, ctxt->iod.out_bytes));
347 #endif
348
349         if (ctxt->alloced)
350                 SAFE_FREE(ctxt);
351         else
352                 ZERO_STRUCTP(ctxt);
353
354         pcd->context = NULL;
355 }
356
357
358 static struct smb_perfcount_handlers onefs_pc_handlers = {
359         onefs_smb_statistics_start,
360         onefs_smb_statistics_add,
361         onefs_smb_statistics_set_op,
362         onefs_smb_statistics_set_subop,
363         onefs_smb_statistics_set_ioctl,
364         onefs_smb_statistics_set_msglen_in,
365         onefs_smb_statistics_set_msglen_out,
366         onefs_smb_statistics_copy_context,
367         onefs_smb_statistics_defer_op,
368         onefs_smb_statistics_end
369 };
370
371 NTSTATUS perfcount_onefs_init(void)
372 {
373         return smb_register_perfcounter(SMB_PERFCOUNTER_INTERFACE_VERSION,
374                                         "pc_onefs", &onefs_pc_handlers);
375 }
376
377 #ifdef ONEFS_PERF_DEBUG
378 /* debug helper */
379 struct op_debug {
380         int type;
381         const char *name;
382 };
383
384 struct op_debug op_debug_table[] = {
385         { 0x00, "mkdir"}, { 0x01, "rmdir"}, { 0x02, "open"}, { 0x03, "create"},
386         { 0x04, "close"}, { 0x05, "flush"}, { 0x06, "unlink"}, { 0x07, "mv"},
387         { 0x08, "getatr"}, { 0x09, "setatr"}, { 0x0a, "read"}, { 0x0b, "write"},
388         { 0x0c, "lock"}, { 0x0d, "unlock"}, { 0x0e, "ctemp"}, { 0x0f, "mknew"},
389         { 0x10, "chkpth"}, { 0x11, "exit"}, { 0x12, "lseek"}, { 0x13, "lockread"},
390         { 0x14, "writeunlock"}, { 0x1a, "readbraw"}, { 0x1b, "readbmpx"},
391         { 0x1c, "readbs"}, { 0x1d, "writebraw"}, { 0x1e, "writebmpx"},
392         { 0x1f, "writebs"}, { 0x20, "writec"}, { 0x22, "setattre"},
393         { 0x23, "getattre"}, { 0x24, "lockingx"}, { 0x25, "trans"},
394         { 0x26, "transs"}, { 0x27, "ioctl"}, { 0x28, "ioctls"}, { 0x29, "copy"},
395         { 0x2a, "move"}, { 0x2b, "echo"}, { 0x2c, "writeclose"}, { 0x2d, "openx"},
396         { 0x2e, "readx"}, { 0x2f, "writex"}, { 0x34, "findclose"},
397         { 0x35, "findnclose"}, { 0x70, "tcon"}, { 0x71, "tdis"},
398         { 0x72, "negprot"}, { 0x73, "sesssetupx"}, { 0x74, "ulogoffx"},
399         { 0x75, "tconx"}, { 0x80, "dskattr"}, { 0x81, "search"},
400         { 0x82, "ffirst"}, { 0x83, "funique"}, { 0x84, "fclose"},
401         { 0x400, "nttrans"},{ 0x500, "nttranss"},
402         { 0xa2, "ntcreatex"}, { 0xa4, "ntcancel"}, { 0xa5, "ntrename"},
403         { 0xc0, "splopen"}, { 0xc1, "splwr"}, { 0xc2, "splclose"},
404         { 0xc3, "splretq"}, { 0xd0, "sends"}, { 0xd1, "sendb"},
405         { 0xd2, "fwdname"}, { 0xd3, "cancelf"}, { 0xd4, "getmac"},
406         { 0xd5, "sendstrt"}, { 0xd6, "sendend"}, { 0xd7, "sendtxt"},
407         { ISP_CIFS_INVALID_OP, "unknown"},
408         { ISP_CIFS_TRNS2 + 0x00,  "trans2:open"},
409         { ISP_CIFS_TRNS2 + 0x01,  "trans2:findfirst"},
410         { ISP_CIFS_TRNS2 + 0x02,  "trans2:findnext"},
411         { ISP_CIFS_TRNS2 + 0x03,  "trans2:qfsinfo"},
412         { ISP_CIFS_TRNS2 + 0x04,  "trans2:setfsinfo"},
413         { ISP_CIFS_TRNS2 + 0x05,  "trans2:qpathinfo"},
414         { ISP_CIFS_TRNS2 + 0x06,  "trans2:setpathinfo"},
415         { ISP_CIFS_TRNS2 + 0x07,  "trans2:qfileinfo"},
416         { ISP_CIFS_TRNS2 + 0x08,  "trans2:setfileinfo"},
417         { ISP_CIFS_TRNS2 + 0x0a,  "trans2:ioctl"},
418         { ISP_CIFS_TRNS2 + 0x0b,  "trans2:findnotifyfirst"},
419         { ISP_CIFS_TRNS2 + 0x0c,  "trans2:findnotifynext"},
420         { ISP_CIFS_TRNS2 + 0x0d,  "trans2:mkdir"},
421         { ISP_CIFS_TRNS2 + 0x10,  "trans2:get_dfs_ref"},
422         { ISP_CIFS_TRNS2 + ISP_CIFS_SUBOP_UNKNOWN, "trans2:unknown"},
423         { ISP_CIFS_TRNSS2 +0x00, "transs2:open"},
424         { ISP_CIFS_TRNSS2 +0x01, "transs2:findfirst"},
425         { ISP_CIFS_TRNSS2 +0x02, "transs2:findnext"},
426         { ISP_CIFS_TRNSS2 +0x03, "transs2:qfsinfo"},
427         { ISP_CIFS_TRNSS2 +0x04, "transs2:setfsinfo"},
428         { ISP_CIFS_TRNSS2 +0x05, "transs2:qpathinfo"},
429         { ISP_CIFS_TRNSS2 +0x06, "transs2:setpathinfo"},
430         { ISP_CIFS_TRNSS2 +0x07, "transs2:qfileinfo"},
431         { ISP_CIFS_TRNSS2 +0x08, "transs2:setfileinfo"},
432         { ISP_CIFS_TRNSS2 +0x0a, "transs2:ioctl"},
433         { ISP_CIFS_TRNSS2 +0x0b, "transs2:findnotifyfirst"},
434         { ISP_CIFS_TRNSS2 +0x0c, "transs2:findnotifynext"},
435         { ISP_CIFS_TRNSS2 +0x0d, "transs2:mkdir"},
436         { ISP_CIFS_TRNSS2 +0x10, "transs2:get_dfs_referral"},
437         { ISP_CIFS_TRNSS2 + ISP_CIFS_SUBOP_UNKNOWN, "transs2:unknown"},
438         { ISP_CIFS_NTRNS + 0x1, "nttrans:create"},
439         { ISP_CIFS_NTRNS + 0x2, "nttrans:ioctl"},
440         { ISP_CIFS_NTRNS + 0x3, "nttrans:set_security_desc"},
441         { ISP_CIFS_NTRNS + 0x4, "nttrans:notify_change"},
442         { ISP_CIFS_NTRNS + 0x5, "nttrans:rename"},
443         { ISP_CIFS_NTRNS + 0x6, "nttrans:qry_security_desc"},
444         { ISP_CIFS_NTRNS + 0x7, "nttrans:get_user_quota"},
445         { ISP_CIFS_NTRNS + 0x8, "nttrans:set_user_quota"},
446         { ISP_CIFS_NTRNS + ISP_CIFS_NTRN_IOCTL_FGSCD,
447                 "nttrans:ioctl:get_shadow_copy_data"},
448         { ISP_CIFS_NTRNS + ISP_CIFS_SUBOP_UNKNOWN,
449                 "nttrans:unknown"},
450         { ISP_CIFS_NTRNSS + 0x1, "nttranss:create"},
451         { ISP_CIFS_NTRNSS + 0x2, "nttranss:ioctl"},
452         { ISP_CIFS_NTRNSS + 0x3, "nttranss:set_security_desc"},
453         { ISP_CIFS_NTRNSS + 0x4, "nttranss:notify_change"},
454         { ISP_CIFS_NTRNSS + 0x5, "nttranss:rename"},
455         { ISP_CIFS_NTRNSS + 0x6, "nttranss:qry_security_desc"},
456         { ISP_CIFS_NTRNSS + 0x7, "nttranss:get_user_quota"},
457         { ISP_CIFS_NTRNSS + 0x8, "nttranss:set_user_quota"},
458         { ISP_CIFS_NTRNSS + ISP_CIFS_NTRN_IOCTL_FGSCD,
459                 "nttranss:ioctl:get_shadow_copy_data"},
460         { ISP_CIFS_NTRNSS + ISP_CIFS_SUBOP_UNKNOWN,
461                 "nttranss:unknown"},
462 };
463
464 int op_debug_table_count = sizeof(op_debug_table) / sizeof(op_debug_table[0]);
465
466 const char *onefs_stat_debug(struct isp_op_delta *iod)
467 {
468         int i;
469         const char *unk = "unknown";
470         for (i=0; i < op_debug_table_count;i++) {
471                 if (iod->op == op_debug_table[i].type)
472                         return op_debug_table[i].name;
473         }
474
475         return unk;
476 }
477 #endif