s3/libsmb: adjust smb2 code for new idl structs & generated ndr push/pull funcs.
[samba.git] / source3 / libsmb / cliquota.c
1 /* 
2    Unix SMB/CIFS implementation.
3    client quota functions
4    Copyright (C) Stefan (metze) Metzmacher      2003
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "includes.h"
21 #include "libsmb/libsmb.h"
22 #include "../librpc/gen_ndr/ndr_security.h"
23 #include "fake_file.h"
24 #include "../libcli/security/security.h"
25 #include "trans2.h"
26 #include "../libcli/smb/smbXcli_base.h"
27 #include "librpc/gen_ndr/ndr_quota.h"
28
29 NTSTATUS cli_get_quota_handle(struct cli_state *cli, uint16_t *quota_fnum)
30 {
31         return cli_ntcreate(cli, FAKE_FILE_NAME_QUOTA_WIN32,
32                  0x00000016, DESIRED_ACCESS_PIPE,
33                  0x00000000, FILE_SHARE_READ|FILE_SHARE_WRITE,
34                  FILE_OPEN, 0x00000000, 0x03, quota_fnum, NULL);
35 }
36
37 void free_ntquota_list(SMB_NTQUOTA_LIST **qt_list)
38 {
39         if (!qt_list || !*qt_list) {
40                 return;
41         }
42
43         if ((*qt_list)->mem_ctx)
44                 talloc_destroy((*qt_list)->mem_ctx);
45
46         (*qt_list) = NULL;
47
48         return; 
49 }
50
51 bool add_record_to_ntquota_list(TALLOC_CTX *mem_ctx,
52                                 SMB_NTQUOTA_STRUCT *pqt,
53                                 SMB_NTQUOTA_LIST **pqt_list)
54 {
55         SMB_NTQUOTA_LIST *tmp_list_ent;
56
57         if ((tmp_list_ent = talloc_zero(mem_ctx, SMB_NTQUOTA_LIST)) == NULL) {
58                 return false;
59         }
60
61         if ((tmp_list_ent->quotas = talloc_zero(mem_ctx, SMB_NTQUOTA_STRUCT)) ==
62             NULL) {
63                 return false;
64         }
65
66         *tmp_list_ent->quotas = *pqt;
67         tmp_list_ent->mem_ctx = mem_ctx;
68
69         DLIST_ADD((*pqt_list), tmp_list_ent);
70
71         return true;
72 }
73
74 bool parse_user_quota_record(const uint8_t *rdata,
75                              unsigned int rdata_count,
76                              unsigned int *offset,
77                              SMB_NTQUOTA_STRUCT *pqt)
78 {
79         struct file_quota_information info = {0};
80         TALLOC_CTX *frame = talloc_stackframe();
81         DATA_BLOB blob;
82         enum ndr_err_code err;
83         bool result = false;
84
85         blob.data = discard_const_p(uint8_t, rdata);
86         blob.length = rdata_count;
87         err = ndr_pull_struct_blob(
88                         &blob,
89                         frame,
90                         &info,
91                         (ndr_pull_flags_fn_t)ndr_pull_file_quota_information);
92
93         if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
94                 goto out;
95         }
96
97         *offset = info.next_entry_offset;
98
99         ZERO_STRUCTP(pqt);
100         pqt->usedspace = info.quota_used;
101
102         pqt->softlim = info.quota_threshold;
103
104         pqt->hardlim = info.quota_limit;
105
106         pqt->qtype = SMB_USER_QUOTA_TYPE;
107         pqt->sid = info.sid;
108         result = true;
109 out:
110         TALLOC_FREE(frame);
111         return result;
112 }
113
114 NTSTATUS parse_user_quota_list(const uint8_t *curdata,
115                                uint32_t curdata_count,
116                                TALLOC_CTX *mem_ctx,
117                                SMB_NTQUOTA_LIST **pqt_list)
118 {
119         NTSTATUS status = NT_STATUS_OK;
120         unsigned offset;
121         SMB_NTQUOTA_STRUCT qt;
122
123         while (true) {
124                 ZERO_STRUCT(qt);
125                 if (!parse_user_quota_record(curdata, curdata_count, &offset,
126                                              &qt)) {
127                         DEBUG(1, ("Failed to parse the quota record\n"));
128                         status = NT_STATUS_INVALID_NETWORK_RESPONSE;
129                         break;
130                 }
131
132                 if (offset > curdata_count) {
133                         DEBUG(1, ("out of bounds offset in quota record\n"));
134                         status = NT_STATUS_INVALID_NETWORK_RESPONSE;
135                         break;
136                 }
137
138                 if (curdata + offset < curdata) {
139                         DEBUG(1, ("Pointer overflow in quota record\n"));
140                         status = NT_STATUS_INVALID_NETWORK_RESPONSE;
141                         break;
142                 }
143
144                 if (!add_record_to_ntquota_list(mem_ctx, &qt, pqt_list)) {
145                         status = NT_STATUS_NO_MEMORY;
146                         break;
147                 }
148
149                 curdata += offset;
150                 curdata_count -= offset;
151
152                 if (offset == 0) {
153                         break;
154                 }
155         }
156
157         return status;
158 }
159
160 NTSTATUS parse_fs_quota_buffer(const uint8_t *rdata,
161                                unsigned int rdata_count,
162                                SMB_NTQUOTA_STRUCT *pqt)
163 {
164         SMB_NTQUOTA_STRUCT qt;
165
166         ZERO_STRUCT(qt);
167
168         if (rdata_count < 48) {
169                 /* minimum length is not enforced by SMB2 client.
170                  */
171                 DEBUG(1, ("small returned fs quota buffer\n"));
172                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
173         }
174
175         /* unknown_1 24 NULL bytes in pdata*/
176
177         /* the soft quotas 8 bytes (uint64_t)*/
178         qt.softlim = BVAL(rdata, 24);
179
180         /* the hard quotas 8 bytes (uint64_t)*/
181         qt.hardlim = BVAL(rdata, 32);
182
183         /* quota_flags 2 bytes **/
184         qt.qflags = SVAL(rdata, 40);
185
186         qt.qtype = SMB_USER_FS_QUOTA_TYPE;
187
188         *pqt = qt;
189
190         return NT_STATUS_OK;
191 }
192
193 NTSTATUS build_user_quota_buffer(SMB_NTQUOTA_LIST *qt_list,
194                                  uint32_t maxlen,
195                                  TALLOC_CTX *mem_ctx,
196                                  DATA_BLOB *outbuf,
197                                  SMB_NTQUOTA_LIST **end_ptr)
198 {
199         return fill_quota_buffer(mem_ctx,
200                                  qt_list,
201                                  false,
202                                  maxlen,
203                                  outbuf,
204                                  end_ptr);
205 }
206
207 NTSTATUS build_fs_quota_buffer(TALLOC_CTX *mem_ctx,
208                                const SMB_NTQUOTA_STRUCT *pqt,
209                                DATA_BLOB *blob,
210                                uint32_t maxlen)
211 {
212         uint8_t *buf;
213
214         if (maxlen > 0 && maxlen < 48) {
215                 return NT_STATUS_BUFFER_TOO_SMALL;
216         }
217
218         *blob = data_blob_talloc_zero(mem_ctx, 48);
219
220         if (!blob->data) {
221                 return NT_STATUS_NO_MEMORY;
222         }
223
224         buf = blob->data;
225
226         /* Unknown1 24 NULL bytes*/
227         SBIG_UINT(buf, 0, (uint64_t)0);
228         SBIG_UINT(buf, 8, (uint64_t)0);
229         SBIG_UINT(buf, 16, (uint64_t)0);
230
231         /* Default Soft Quota 8 bytes */
232         SBIG_UINT(buf, 24, pqt->softlim);
233
234         /* Default Hard Quota 8 bytes */
235         SBIG_UINT(buf, 32, pqt->hardlim);
236
237         /* Quota flag 4 bytes */
238         SIVAL(buf, 40, pqt->qflags);
239
240         /* 4 padding bytes */
241         SIVAL(buf, 44, 0);
242
243         return NT_STATUS_OK;
244 }
245
246 NTSTATUS cli_get_user_quota(struct cli_state *cli, int quota_fnum,
247                             SMB_NTQUOTA_STRUCT *pqt)
248 {
249         uint16_t setup[1];
250         uint8_t *rparam = NULL, *rdata = NULL;
251         uint32_t rparam_count, rdata_count;
252         unsigned int sid_len;
253         unsigned int offset;
254         struct nttrans_query_quota_params get_quota = {0};
255         struct file_get_quota_info info =  {0};
256         enum ndr_err_code err;
257         struct ndr_push *ndr_push = NULL;
258         NTSTATUS status;
259         TALLOC_CTX *frame = talloc_stackframe();
260         DATA_BLOB data_blob = data_blob_null;
261
262         if (!cli||!pqt) {
263                 smb_panic("cli_get_user_quota() called with NULL Pointer!");
264         }
265
266         if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
267                 TALLOC_FREE(frame);
268                 return cli_smb2_get_user_quota(cli, quota_fnum, pqt);
269         }
270
271         get_quota.fid = quota_fnum;
272         get_quota.return_single_entry = 1;
273         get_quota.restart_scan = 0;
274
275         sid_len = ndr_size_dom_sid(&pqt->sid, 0);
276
277         info.next_entry_offset = 0;
278         info.sid_length = sid_len;
279         info.sid = pqt->sid;
280
281         err = ndr_push_struct_blob(
282                         &data_blob,
283                         frame,
284                         &info,
285                         (ndr_push_flags_fn_t)ndr_push_file_get_quota_info);
286
287         if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
288                 status = NT_STATUS_INTERNAL_ERROR;
289                 goto out;
290         }
291
292         get_quota.sid_list_length = data_blob.length;
293         get_quota.start_sid_offset = data_blob.length;
294
295         ndr_push = ndr_push_init_ctx(frame);
296
297         if (!ndr_push) {
298                 status = NT_STATUS_NO_MEMORY;
299                 goto out;
300         }
301
302         err = ndr_push_nttrans_query_quota_params(ndr_push,
303                                              NDR_SCALARS | NDR_BUFFERS,
304                                              &get_quota);
305
306         if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
307                 status = NT_STATUS_INTERNAL_ERROR;
308                 goto out;
309         }
310
311         status = cli_trans(talloc_tos(), cli, SMBnttrans,
312                            NULL, -1, /* name, fid */
313                            NT_TRANSACT_GET_USER_QUOTA, 0,
314                            setup, 1, 0, /* setup */
315                            ndr_push->data, ndr_push->offset, 4, /* params */
316                            data_blob.data, data_blob.length, 112, /* data */
317                            NULL,                /* recv_flags2 */
318                            NULL, 0, NULL,       /* rsetup */
319                            &rparam, 4, &rparam_count,
320                            &rdata, 8, &rdata_count);
321         if (!NT_STATUS_IS_OK(status)) {
322                 DEBUG(1, ("NT_TRANSACT_GET_USER_QUOTA failed: %s\n",
323                           nt_errstr(status)));
324                 goto out;
325         }
326
327         if (!parse_user_quota_record(rdata, rdata_count, &offset, pqt)) {
328                 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
329                 DEBUG(0,("Got INVALID NT_TRANSACT_GET_USER_QUOTA reply.\n"));
330         }
331
332 out:
333         TALLOC_FREE(rparam);
334         TALLOC_FREE(rdata);
335         TALLOC_FREE(frame);
336         return status;
337 }
338
339 NTSTATUS
340 cli_set_user_quota(struct cli_state *cli, int quota_fnum, SMB_NTQUOTA_LIST *qtl)
341 {
342         uint16_t setup[1];
343         uint8_t params[2];
344         DATA_BLOB data = data_blob_null;
345         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
346
347         if (!cli || !qtl) {
348                 smb_panic("cli_set_user_quota() called with NULL Pointer!");
349         }
350
351         if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
352                 return cli_smb2_set_user_quota(cli, quota_fnum, qtl);
353         }
354
355         status = build_user_quota_buffer(qtl, 0, talloc_tos(), &data, NULL);
356         if (!NT_STATUS_IS_OK(status)) {
357                 /*
358                  * smb1 doesn't send NT_STATUS_NO_MORE_ENTRIES so swallow
359                  * this status.
360                  */
361                 if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) {
362                         status = NT_STATUS_OK;
363                 } else {
364                         goto cleanup;
365                 }
366         }
367
368         SSVAL(setup + 0, 0, NT_TRANSACT_SET_USER_QUOTA);
369
370         SSVAL(params,0,quota_fnum);
371
372         status = cli_trans(talloc_tos(), cli, SMBnttrans,
373                            NULL, -1, /* name, fid */
374                            NT_TRANSACT_SET_USER_QUOTA, 0,
375                            setup, 1, 0, /* setup */
376                            params, 2, 0, /* params */
377                            data.data, data.length, 0, /* data */
378                            NULL,                /* recv_flags2 */
379                            NULL, 0, NULL,       /* rsetup */
380                            NULL, 0, NULL,       /* rparams */
381                            NULL, 0, NULL);      /* rdata */
382
383         if (!NT_STATUS_IS_OK(status)) {
384                 DEBUG(1, ("NT_TRANSACT_SET_USER_QUOTA failed: %s\n",
385                           nt_errstr(status)));
386         }
387
388 cleanup:
389         data_blob_free(&data);
390         return status;
391 }
392
393 static NTSTATUS cli_list_user_quota_step(struct cli_state *cli,
394                                          TALLOC_CTX *mem_ctx,
395                                          int quota_fnum,
396                                          SMB_NTQUOTA_LIST **pqt_list,
397                                          bool first)
398 {
399         uint16_t setup[1];
400         DATA_BLOB params_blob = data_blob_null;
401         uint8_t *rparam=NULL, *rdata=NULL;
402         uint32_t rparam_count=0, rdata_count=0;
403         NTSTATUS status;
404         struct nttrans_query_quota_params quota_params = {0};
405         enum ndr_err_code err;
406
407         TALLOC_CTX *frame = NULL;
408         if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
409                 return cli_smb2_list_user_quota_step(cli, mem_ctx, quota_fnum,
410                                                      pqt_list, first);
411         }
412         frame = talloc_stackframe();
413
414         SSVAL(setup + 0, 0, NT_TRANSACT_GET_USER_QUOTA);
415
416         quota_params.fid = quota_fnum;
417         if (first) {
418                 quota_params.restart_scan = 1;
419         }
420         err = ndr_push_struct_blob(
421                 &params_blob,
422                 frame,
423                 &quota_params,
424                 (ndr_push_flags_fn_t)ndr_push_nttrans_query_quota_params);
425
426         if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
427                 status = NT_STATUS_INVALID_PARAMETER;
428                 goto cleanup;
429         }
430
431         status = cli_trans(talloc_tos(), cli, SMBnttrans,
432                            NULL, -1, /* name, fid */
433                            NT_TRANSACT_GET_USER_QUOTA, 0,
434                            setup, 1, 0, /* setup */
435                            params_blob.data, params_blob.length, 4, /* params */
436                            NULL, 0, 2048, /* data */
437                            NULL,                /* recv_flags2 */
438                            NULL, 0, NULL,       /* rsetup */
439                            &rparam, 0, &rparam_count,
440                            &rdata, 0, &rdata_count);
441
442         /* compat. with smbd + safeguard against
443          * endless loop
444          */
445         if (NT_STATUS_IS_OK(status) && rdata_count == 0) {
446                 status = NT_STATUS_NO_MORE_ENTRIES;
447         }
448
449         if (!NT_STATUS_IS_OK(status)) {
450                 goto cleanup;
451         }
452
453         status = parse_user_quota_list(rdata, rdata_count, mem_ctx, pqt_list);
454
455 cleanup:
456         TALLOC_FREE(rparam);
457         TALLOC_FREE(rdata);
458         TALLOC_FREE(frame);
459
460         return status;
461 }
462
463 NTSTATUS cli_list_user_quota(struct cli_state *cli,
464                              int quota_fnum,
465                              SMB_NTQUOTA_LIST **pqt_list)
466 {
467         NTSTATUS status;
468         TALLOC_CTX *mem_ctx = NULL;
469         bool first = true;
470
471         if (!cli || !pqt_list) {
472                 smb_panic("cli_list_user_quota() called with NULL Pointer!");
473         }
474
475         *pqt_list = NULL;
476
477         if ((mem_ctx = talloc_init("SMB_USER_QUOTA_LIST")) == NULL) {
478                 return NT_STATUS_NO_MEMORY;
479         }
480
481         do {
482                 status = cli_list_user_quota_step(cli, mem_ctx, quota_fnum,
483                                                   pqt_list, first);
484                 first = false;
485         } while (NT_STATUS_IS_OK(status));
486
487         if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) {
488                 status = NT_STATUS_OK;
489         }
490
491         if (!NT_STATUS_IS_OK(status) || *pqt_list == NULL) {
492                 TALLOC_FREE(mem_ctx);
493         }
494
495         return status;
496 }
497
498 NTSTATUS cli_get_fs_quota_info(struct cli_state *cli, int quota_fnum,
499                                SMB_NTQUOTA_STRUCT *pqt)
500 {
501         uint16_t setup[1];
502         uint8_t param[2];
503         uint8_t *rdata=NULL;
504         uint32_t rdata_count=0;
505         NTSTATUS status;
506
507         if (!cli||!pqt) {
508                 smb_panic("cli_get_fs_quota_info() called with NULL Pointer!");
509         }
510
511         if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
512                 return cli_smb2_get_fs_quota_info(cli, quota_fnum, pqt);
513         }
514
515         SSVAL(setup + 0, 0, TRANSACT2_QFSINFO);
516
517         SSVAL(param,0,SMB_FS_QUOTA_INFORMATION);
518
519         status = cli_trans(talloc_tos(), cli, SMBtrans2,
520                            NULL, -1, /* name, fid */
521                            0, 0,     /* function, flags */
522                            setup, 1, 0, /* setup */
523                            param, 2, 0, /* param */
524                            NULL, 0, 560, /* data */
525                            NULL,         /* recv_flags2 */
526                            NULL, 0, NULL, /* rsetup */
527                            NULL, 0, NULL, /* rparam */
528                            &rdata, 48, &rdata_count);
529
530         if (!NT_STATUS_IS_OK(status)) {
531                 DEBUG(1, ("SMB_FS_QUOTA_INFORMATION failed: %s\n",
532                           nt_errstr(status)));
533                 return status;
534         }
535
536         status = parse_fs_quota_buffer(rdata, rdata_count, pqt);
537
538         TALLOC_FREE(rdata);
539         return status;
540 }
541
542 NTSTATUS cli_set_fs_quota_info(struct cli_state *cli, int quota_fnum,
543                                SMB_NTQUOTA_STRUCT *pqt)
544 {
545         uint16_t setup[1];
546         uint8_t param[4];
547         DATA_BLOB data = data_blob_null;
548         NTSTATUS status;
549
550         if (!cli||!pqt) {
551                 smb_panic("cli_set_fs_quota_info() called with NULL Pointer!");
552         }
553
554         if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
555                 return cli_smb2_set_fs_quota_info(cli, quota_fnum, pqt);
556         }
557
558         status = build_fs_quota_buffer(talloc_tos(), pqt, &data, 0);
559         if (!NT_STATUS_IS_OK(status)) {
560                 return status;
561         }
562
563         SSVAL(setup + 0, 0,TRANSACT2_SETFSINFO);
564
565         SSVAL(param,0,quota_fnum);
566         SSVAL(param,2,SMB_FS_QUOTA_INFORMATION);
567
568         status = cli_trans(talloc_tos(), cli, SMBtrans2,
569                            NULL, -1, /* name, fid */
570                            0, 0,     /* function, flags */
571                            setup, 1, 0, /* setup */
572                            param, 4, 0, /* param */
573                            data.data, data.length, 0, /* data */
574                            NULL,         /* recv_flags2 */
575                            NULL, 0, NULL, /* rsetup */
576                            NULL, 0, NULL, /* rparam */
577                            NULL, 0, NULL); /* rdata */
578
579         if (!NT_STATUS_IS_OK(status)) {
580                 DEBUG(1, ("SMB_FS_QUOTA_INFORMATION failed: %s\n",
581                           nt_errstr(status)));
582         }
583
584         return status;
585 }
586
587 NTSTATUS fill_quota_buffer(TALLOC_CTX *mem_ctx,
588                               SMB_NTQUOTA_LIST *qlist,
589                               bool return_single,
590                               uint32_t max_data,
591                               DATA_BLOB *blob,
592                               SMB_NTQUOTA_LIST **end_ptr)
593 {
594         int ndr_flags = NDR_SCALARS | NDR_BUFFERS;
595         struct ndr_push *qndr = ndr_push_init_ctx(mem_ctx);
596         uint32_t start_offset = 0;
597         uint32_t padding = 0;
598         if (qlist == NULL) {
599                 /* We must push at least one. */
600                 return NT_STATUS_NO_MORE_ENTRIES;
601         }
602         for (;qlist != NULL; qlist = qlist->next) {
603                 struct file_quota_information info = {0};
604                 enum ndr_err_code err;
605                 uint32_t dsize = sizeof(info.next_entry_offset)
606                         + sizeof(info.sid_length)
607                         + sizeof(info.change_time)
608                         + sizeof(info.quota_used)
609                         + sizeof(info.quota_threshold)
610                         + sizeof(info.quota_limit);
611
612
613                 info.sid_length = ndr_size_dom_sid(&qlist->quotas->sid, 0);
614
615                 if (max_data) {
616                         uint32_t curr_pos_no_padding = qndr->offset - padding;
617                         uint32_t payload = dsize + info.sid_length;
618                         uint32_t new_pos = (curr_pos_no_padding + payload);
619                         if (new_pos < curr_pos_no_padding) {
620                                 /* Detect unlikely integer wrap */
621                                 DBG_ERR("Integer wrap while adjusting pos "
622                                         "0x%x by offset 0x%x\n",
623                                         curr_pos_no_padding, payload);
624                                 return NT_STATUS_INTERNAL_ERROR;
625                         }
626                         if (new_pos > max_data) {
627                                 DBG_WARNING("Max data will be exceeded "
628                                             "writing next query info. "
629                                             "cur_pos 0x%x, sid_length 0x%x, "
630                                             "dsize 0x%x, max_data 0x%x\n",
631                                             curr_pos_no_padding,
632                                             info.sid_length,
633                                             dsize,
634                                             max_data);
635                                 break;
636                         }
637                 }
638
639                 start_offset = qndr->offset;
640                 info.sid = qlist->quotas->sid;
641                 info.quota_used = qlist->quotas->usedspace;
642                 info.quota_threshold = qlist->quotas->softlim;
643                 info.quota_limit = qlist->quotas->hardlim;
644
645                 err = ndr_push_file_quota_information(qndr,
646                                                       ndr_flags,
647                                                       &info);
648
649                 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
650                         DBG_DEBUG("Failed to push the quota sid\n");
651                         return NT_STATUS_INTERNAL_ERROR;
652                 }
653
654                 /* pidl will align to 8 bytes due to 8 byte members*/
655                 /* Remember how much align padding we've used. */
656                 padding = qndr->offset;
657                 ndr_push_align(qndr, 8);
658                 padding = qndr->offset - padding;
659
660                 /*
661                  * Overwrite next_entry_offset for this entry now
662                  * we know what it should be. We know we're using
663                  * LIBNDR_FLAG_LITTLE_ENDIAN here so we can use
664                  * SIVAL.
665                  */
666                 info.next_entry_offset = qndr->offset - start_offset;
667                 SIVAL(qndr->data, start_offset, info.next_entry_offset);
668
669                 if (return_single) {
670                         break;
671                 }
672         }
673
674         if (end_ptr != NULL) {
675                 *end_ptr = qlist;
676         }
677
678         /* Remove the padding alignment on the last element pushed. */
679         blob->length = qndr->offset - padding;
680         blob->data = qndr->data;
681
682         /*
683          * Terminate the pushed array by setting next_entry_offset
684          * for the last element to zero.
685          */
686         if (blob->length >= sizeof(uint32_t)) {
687                 SIVAL(qndr->data, start_offset, 0);
688         }
689         return NT_STATUS_OK;
690 }