lib: remove unused function nttime_from_string()
[bbaumbach/samba-autobuild/.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         NTSTATUS status;
258         TALLOC_CTX *frame = talloc_stackframe();
259         DATA_BLOB data_blob = data_blob_null;
260         DATA_BLOB param_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         err = ndr_push_struct_blob(
296                 &param_blob,
297                 frame,
298                 &get_quota,
299                 (ndr_push_flags_fn_t)ndr_push_nttrans_query_quota_params);
300
301         if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
302                 status = NT_STATUS_INTERNAL_ERROR;
303                 goto out;
304         }
305
306         status = cli_trans(talloc_tos(), cli, SMBnttrans,
307                            NULL, -1, /* name, fid */
308                            NT_TRANSACT_GET_USER_QUOTA, 0,
309                            setup, 1, 0, /* setup */
310                            param_blob.data, param_blob.length, 4, /* params */
311                            data_blob.data, data_blob.length, 112, /* data */
312                            NULL,                /* recv_flags2 */
313                            NULL, 0, NULL,       /* rsetup */
314                            &rparam, 4, &rparam_count,
315                            &rdata, 8, &rdata_count);
316         if (!NT_STATUS_IS_OK(status)) {
317                 DEBUG(1, ("NT_TRANSACT_GET_USER_QUOTA failed: %s\n",
318                           nt_errstr(status)));
319                 goto out;
320         }
321
322         if (!parse_user_quota_record(rdata, rdata_count, &offset, pqt)) {
323                 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
324                 DEBUG(0,("Got INVALID NT_TRANSACT_GET_USER_QUOTA reply.\n"));
325         }
326
327 out:
328         TALLOC_FREE(rparam);
329         TALLOC_FREE(rdata);
330         TALLOC_FREE(frame);
331         return status;
332 }
333
334 NTSTATUS
335 cli_set_user_quota(struct cli_state *cli, int quota_fnum, SMB_NTQUOTA_LIST *qtl)
336 {
337         uint16_t setup[1];
338         uint8_t params[2];
339         DATA_BLOB data = data_blob_null;
340         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
341
342         if (!cli || !qtl) {
343                 smb_panic("cli_set_user_quota() called with NULL Pointer!");
344         }
345
346         if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
347                 return cli_smb2_set_user_quota(cli, quota_fnum, qtl);
348         }
349
350         status = build_user_quota_buffer(qtl, 0, talloc_tos(), &data, NULL);
351         if (!NT_STATUS_IS_OK(status)) {
352                 /*
353                  * smb1 doesn't send NT_STATUS_NO_MORE_ENTRIES so swallow
354                  * this status.
355                  */
356                 if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) {
357                         status = NT_STATUS_OK;
358                 } else {
359                         goto cleanup;
360                 }
361         }
362
363         SSVAL(setup + 0, 0, NT_TRANSACT_SET_USER_QUOTA);
364
365         SSVAL(params,0,quota_fnum);
366
367         status = cli_trans(talloc_tos(), cli, SMBnttrans,
368                            NULL, -1, /* name, fid */
369                            NT_TRANSACT_SET_USER_QUOTA, 0,
370                            setup, 1, 0, /* setup */
371                            params, 2, 0, /* params */
372                            data.data, data.length, 0, /* data */
373                            NULL,                /* recv_flags2 */
374                            NULL, 0, NULL,       /* rsetup */
375                            NULL, 0, NULL,       /* rparams */
376                            NULL, 0, NULL);      /* rdata */
377
378         if (!NT_STATUS_IS_OK(status)) {
379                 DEBUG(1, ("NT_TRANSACT_SET_USER_QUOTA failed: %s\n",
380                           nt_errstr(status)));
381         }
382
383 cleanup:
384         data_blob_free(&data);
385         return status;
386 }
387
388 static NTSTATUS cli_list_user_quota_step(struct cli_state *cli,
389                                          TALLOC_CTX *mem_ctx,
390                                          int quota_fnum,
391                                          SMB_NTQUOTA_LIST **pqt_list,
392                                          bool first)
393 {
394         uint16_t setup[1];
395         DATA_BLOB params_blob = data_blob_null;
396         uint8_t *rparam=NULL, *rdata=NULL;
397         uint32_t rparam_count=0, rdata_count=0;
398         NTSTATUS status;
399         struct nttrans_query_quota_params quota_params = {0};
400         enum ndr_err_code err;
401
402         TALLOC_CTX *frame = NULL;
403         if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
404                 return cli_smb2_list_user_quota_step(cli, mem_ctx, quota_fnum,
405                                                      pqt_list, first);
406         }
407         frame = talloc_stackframe();
408
409         SSVAL(setup + 0, 0, NT_TRANSACT_GET_USER_QUOTA);
410
411         quota_params.fid = quota_fnum;
412         if (first) {
413                 quota_params.restart_scan = 1;
414         }
415         err = ndr_push_struct_blob(
416                 &params_blob,
417                 frame,
418                 &quota_params,
419                 (ndr_push_flags_fn_t)ndr_push_nttrans_query_quota_params);
420
421         if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
422                 status = NT_STATUS_INVALID_PARAMETER;
423                 goto cleanup;
424         }
425
426         status = cli_trans(talloc_tos(), cli, SMBnttrans,
427                            NULL, -1, /* name, fid */
428                            NT_TRANSACT_GET_USER_QUOTA, 0,
429                            setup, 1, 0, /* setup */
430                            params_blob.data, params_blob.length, 4, /* params */
431                            NULL, 0, 2048, /* data */
432                            NULL,                /* recv_flags2 */
433                            NULL, 0, NULL,       /* rsetup */
434                            &rparam, 0, &rparam_count,
435                            &rdata, 0, &rdata_count);
436
437         /* compat. with smbd + safeguard against
438          * endless loop
439          */
440         if (NT_STATUS_IS_OK(status) && rdata_count == 0) {
441                 status = NT_STATUS_NO_MORE_ENTRIES;
442         }
443
444         if (!NT_STATUS_IS_OK(status)) {
445                 goto cleanup;
446         }
447
448         status = parse_user_quota_list(rdata, rdata_count, mem_ctx, pqt_list);
449
450 cleanup:
451         TALLOC_FREE(rparam);
452         TALLOC_FREE(rdata);
453         TALLOC_FREE(frame);
454
455         return status;
456 }
457
458 NTSTATUS cli_list_user_quota(struct cli_state *cli,
459                              int quota_fnum,
460                              SMB_NTQUOTA_LIST **pqt_list)
461 {
462         NTSTATUS status;
463         TALLOC_CTX *mem_ctx = NULL;
464         bool first = true;
465
466         if (!cli || !pqt_list) {
467                 smb_panic("cli_list_user_quota() called with NULL Pointer!");
468         }
469
470         *pqt_list = NULL;
471
472         if ((mem_ctx = talloc_init("SMB_USER_QUOTA_LIST")) == NULL) {
473                 return NT_STATUS_NO_MEMORY;
474         }
475
476         do {
477                 status = cli_list_user_quota_step(cli, mem_ctx, quota_fnum,
478                                                   pqt_list, first);
479                 first = false;
480         } while (NT_STATUS_IS_OK(status));
481
482         if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) {
483                 status = NT_STATUS_OK;
484         }
485
486         if (!NT_STATUS_IS_OK(status) || *pqt_list == NULL) {
487                 TALLOC_FREE(mem_ctx);
488         }
489
490         return status;
491 }
492
493 NTSTATUS cli_get_fs_quota_info(struct cli_state *cli, int quota_fnum,
494                                SMB_NTQUOTA_STRUCT *pqt)
495 {
496         uint16_t setup[1];
497         uint8_t param[2];
498         uint8_t *rdata=NULL;
499         uint32_t rdata_count=0;
500         NTSTATUS status;
501
502         if (!cli||!pqt) {
503                 smb_panic("cli_get_fs_quota_info() called with NULL Pointer!");
504         }
505
506         if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
507                 return cli_smb2_get_fs_quota_info(cli, quota_fnum, pqt);
508         }
509
510         SSVAL(setup + 0, 0, TRANSACT2_QFSINFO);
511
512         SSVAL(param,0,SMB_FS_QUOTA_INFORMATION);
513
514         status = cli_trans(talloc_tos(), cli, SMBtrans2,
515                            NULL, -1, /* name, fid */
516                            0, 0,     /* function, flags */
517                            setup, 1, 0, /* setup */
518                            param, 2, 0, /* param */
519                            NULL, 0, 560, /* data */
520                            NULL,         /* recv_flags2 */
521                            NULL, 0, NULL, /* rsetup */
522                            NULL, 0, NULL, /* rparam */
523                            &rdata, 48, &rdata_count);
524
525         if (!NT_STATUS_IS_OK(status)) {
526                 DEBUG(1, ("SMB_FS_QUOTA_INFORMATION failed: %s\n",
527                           nt_errstr(status)));
528                 return status;
529         }
530
531         status = parse_fs_quota_buffer(rdata, rdata_count, pqt);
532
533         TALLOC_FREE(rdata);
534         return status;
535 }
536
537 NTSTATUS cli_set_fs_quota_info(struct cli_state *cli, int quota_fnum,
538                                SMB_NTQUOTA_STRUCT *pqt)
539 {
540         uint16_t setup[1];
541         uint8_t param[4];
542         DATA_BLOB data = data_blob_null;
543         NTSTATUS status;
544
545         if (!cli||!pqt) {
546                 smb_panic("cli_set_fs_quota_info() called with NULL Pointer!");
547         }
548
549         if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
550                 return cli_smb2_set_fs_quota_info(cli, quota_fnum, pqt);
551         }
552
553         status = build_fs_quota_buffer(talloc_tos(), pqt, &data, 0);
554         if (!NT_STATUS_IS_OK(status)) {
555                 return status;
556         }
557
558         SSVAL(setup + 0, 0,TRANSACT2_SETFSINFO);
559
560         SSVAL(param,0,quota_fnum);
561         SSVAL(param,2,SMB_FS_QUOTA_INFORMATION);
562
563         status = cli_trans(talloc_tos(), cli, SMBtrans2,
564                            NULL, -1, /* name, fid */
565                            0, 0,     /* function, flags */
566                            setup, 1, 0, /* setup */
567                            param, 4, 0, /* param */
568                            data.data, data.length, 0, /* data */
569                            NULL,         /* recv_flags2 */
570                            NULL, 0, NULL, /* rsetup */
571                            NULL, 0, NULL, /* rparam */
572                            NULL, 0, NULL); /* rdata */
573
574         if (!NT_STATUS_IS_OK(status)) {
575                 DEBUG(1, ("SMB_FS_QUOTA_INFORMATION failed: %s\n",
576                           nt_errstr(status)));
577         }
578
579         return status;
580 }
581
582 NTSTATUS fill_quota_buffer(TALLOC_CTX *mem_ctx,
583                               SMB_NTQUOTA_LIST *qlist,
584                               bool return_single,
585                               uint32_t max_data,
586                               DATA_BLOB *blob,
587                               SMB_NTQUOTA_LIST **end_ptr)
588 {
589         int ndr_flags = NDR_SCALARS | NDR_BUFFERS;
590         struct ndr_push *qndr = ndr_push_init_ctx(mem_ctx);
591         uint32_t start_offset = 0;
592         uint32_t padding = 0;
593         if (qlist == NULL) {
594                 /* We must push at least one. */
595                 return NT_STATUS_NO_MORE_ENTRIES;
596         }
597         for (;qlist != NULL; qlist = qlist->next) {
598                 struct file_quota_information info = {0};
599                 enum ndr_err_code err;
600                 uint32_t dsize = sizeof(info.next_entry_offset)
601                         + sizeof(info.sid_length)
602                         + sizeof(info.change_time)
603                         + sizeof(info.quota_used)
604                         + sizeof(info.quota_threshold)
605                         + sizeof(info.quota_limit);
606
607
608                 info.sid_length = ndr_size_dom_sid(&qlist->quotas->sid, 0);
609
610                 if (max_data) {
611                         uint32_t curr_pos_no_padding = qndr->offset - padding;
612                         uint32_t payload = dsize + info.sid_length;
613                         uint32_t new_pos = (curr_pos_no_padding + payload);
614                         if (new_pos < curr_pos_no_padding) {
615                                 /* Detect unlikely integer wrap */
616                                 DBG_ERR("Integer wrap while adjusting pos "
617                                         "0x%x by offset 0x%x\n",
618                                         curr_pos_no_padding, payload);
619                                 return NT_STATUS_INTERNAL_ERROR;
620                         }
621                         if (new_pos > max_data) {
622                                 DBG_WARNING("Max data will be exceeded "
623                                             "writing next query info. "
624                                             "cur_pos 0x%x, sid_length 0x%x, "
625                                             "dsize 0x%x, max_data 0x%x\n",
626                                             curr_pos_no_padding,
627                                             info.sid_length,
628                                             dsize,
629                                             max_data);
630                                 break;
631                         }
632                 }
633
634                 start_offset = qndr->offset;
635                 info.sid = qlist->quotas->sid;
636                 info.quota_used = qlist->quotas->usedspace;
637                 info.quota_threshold = qlist->quotas->softlim;
638                 info.quota_limit = qlist->quotas->hardlim;
639
640                 err = ndr_push_file_quota_information(qndr,
641                                                       ndr_flags,
642                                                       &info);
643
644                 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
645                         DBG_DEBUG("Failed to push the quota sid\n");
646                         return NT_STATUS_INTERNAL_ERROR;
647                 }
648
649                 /* pidl will align to 8 bytes due to 8 byte members*/
650                 /* Remember how much align padding we've used. */
651                 padding = qndr->offset;
652
653                 err = ndr_push_align(qndr, 8);
654                 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
655                         DBG_DEBUG("ndr_push_align returned %s\n",
656                                   ndr_map_error2string(err));
657                         return ndr_map_error2ntstatus(err);
658                 }
659
660                 padding = qndr->offset - padding;
661
662                 /*
663                  * Overwrite next_entry_offset for this entry now
664                  * we know what it should be. We know we're using
665                  * LIBNDR_FLAG_LITTLE_ENDIAN here so we can use
666                  * SIVAL.
667                  */
668                 info.next_entry_offset = qndr->offset - start_offset;
669                 SIVAL(qndr->data, start_offset, info.next_entry_offset);
670
671                 if (return_single) {
672                         break;
673                 }
674         }
675
676         if (end_ptr != NULL) {
677                 *end_ptr = qlist;
678         }
679
680         /* Remove the padding alignment on the last element pushed. */
681         blob->length = qndr->offset - padding;
682         blob->data = qndr->data;
683
684         /*
685          * Terminate the pushed array by setting next_entry_offset
686          * for the last element to zero.
687          */
688         if (blob->length >= sizeof(uint32_t)) {
689                 SIVAL(qndr->data, start_offset, 0);
690         }
691         return NT_STATUS_OK;
692 }