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