Get medieval on our ass about SMB1 file descriptors being 16 bits, not an int.
[jra/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
22 NTSTATUS cli_get_quota_handle(struct cli_state *cli, uint16_t *quota_fnum)
23 {
24         return cli_ntcreate(cli, FAKE_FILE_NAME_QUOTA_WIN32,
25                  0x00000016, DESIRED_ACCESS_PIPE,
26                  0x00000000, FILE_SHARE_READ|FILE_SHARE_WRITE,
27                  FILE_OPEN, 0x00000000, 0x03, quota_fnum);
28 }
29
30 void free_ntquota_list(SMB_NTQUOTA_LIST **qt_list)
31 {
32         if (!qt_list)
33                 return;
34
35         if ((*qt_list)->mem_ctx)
36                 talloc_destroy((*qt_list)->mem_ctx);
37
38         (*qt_list) = NULL;
39
40         return; 
41 }
42
43 static bool parse_user_quota_record(const char *rdata, unsigned int rdata_count, unsigned int *offset, SMB_NTQUOTA_STRUCT *pqt)
44 {
45         int sid_len;
46         SMB_NTQUOTA_STRUCT qt;
47
48         ZERO_STRUCT(qt);
49
50         if (!rdata||!offset||!pqt) {
51                 smb_panic("parse_quota_record: called with NULL POINTER!");
52         }
53
54         if (rdata_count < 40) {
55                 return False;
56         }
57
58         /* offset to next quota record.
59          * 4 bytes IVAL(rdata,0)
60          * unused here...
61          */
62         *offset = IVAL(rdata,0);
63
64         /* sid len */
65         sid_len = IVAL(rdata,4);
66
67         if (rdata_count < 40+sid_len) {
68                 return False;           
69         }
70
71         /* unknown 8 bytes in pdata 
72          * maybe its the change time in NTTIME
73          */
74
75         /* the used space 8 bytes (uint64_t)*/
76         qt.usedspace = (uint64_t)IVAL(rdata,16);
77 #ifdef LARGE_SMB_OFF_T
78         qt.usedspace |= (((uint64_t)IVAL(rdata,20)) << 32);
79 #else /* LARGE_SMB_OFF_T */
80         if ((IVAL(rdata,20) != 0)&&
81                 ((qt.usedspace != 0xFFFFFFFF)||
82                  (IVAL(rdata,20)!=0xFFFFFFFF))) {
83                 /* more than 32 bits? */
84                 return False;
85         }
86 #endif /* LARGE_SMB_OFF_T */
87
88         /* the soft quotas 8 bytes (uint64_t)*/
89         qt.softlim = (uint64_t)IVAL(rdata,24);
90 #ifdef LARGE_SMB_OFF_T
91         qt.softlim |= (((uint64_t)IVAL(rdata,28)) << 32);
92 #else /* LARGE_SMB_OFF_T */
93         if ((IVAL(rdata,28) != 0)&&
94                 ((qt.softlim != 0xFFFFFFFF)||
95                  (IVAL(rdata,28)!=0xFFFFFFFF))) {
96                 /* more than 32 bits? */
97                 return False;
98         }
99 #endif /* LARGE_SMB_OFF_T */
100
101         /* the hard quotas 8 bytes (uint64_t)*/
102         qt.hardlim = (uint64_t)IVAL(rdata,32);
103 #ifdef LARGE_SMB_OFF_T
104         qt.hardlim |= (((uint64_t)IVAL(rdata,36)) << 32);
105 #else /* LARGE_SMB_OFF_T */
106         if ((IVAL(rdata,36) != 0)&&
107                 ((qt.hardlim != 0xFFFFFFFF)||
108                  (IVAL(rdata,36)!=0xFFFFFFFF))) {
109                 /* more than 32 bits? */
110                 return False;
111         }
112 #endif /* LARGE_SMB_OFF_T */
113
114         sid_parse(rdata+40,sid_len,&qt.sid);
115
116         qt.qtype = SMB_USER_QUOTA_TYPE;
117
118         *pqt = qt;
119
120         return True;
121 }
122
123 bool cli_get_user_quota(struct cli_state *cli, int quota_fnum, SMB_NTQUOTA_STRUCT *pqt)
124 {
125         bool ret = False;
126         uint16 setup;
127         char params[16];
128         unsigned int data_len;
129         char data[SID_MAX_SIZE+8];
130         char *rparam=NULL, *rdata=NULL;
131         unsigned int rparam_count=0, rdata_count=0;
132         unsigned int sid_len;
133         unsigned int offset;
134
135         if (!cli||!pqt) {
136                 smb_panic("cli_get_user_quota() called with NULL Pointer!");
137         }
138
139         setup = NT_TRANSACT_GET_USER_QUOTA;
140
141         SSVAL(params, 0,quota_fnum);
142         SSVAL(params, 2,TRANSACT_GET_USER_QUOTA_FOR_SID);
143         SIVAL(params, 4,0x00000024);
144         SIVAL(params, 8,0x00000000);
145         SIVAL(params,12,0x00000024);
146
147         sid_len = ndr_size_dom_sid(&pqt->sid, NULL, 0);
148         data_len = sid_len+8;
149         SIVAL(data, 0, 0x00000000);
150         SIVAL(data, 4, sid_len);
151         sid_linearize(data+8, sid_len, &pqt->sid);
152
153         if (!cli_send_nt_trans(cli, 
154                                NT_TRANSACT_GET_USER_QUOTA, 
155                                0, 
156                                &setup, 1, 0,
157                                params, 16, 4,
158                                data, data_len, 112)) {
159                 DEBUG(1,("Failed to send NT_TRANSACT_GET_USER_QUOTA\n"));
160                 goto cleanup;
161         }
162
163
164         if (!cli_receive_nt_trans(cli,
165                                   &rparam, &rparam_count,
166                                   &rdata, &rdata_count)) {
167                 DEBUG(1,("Failed to recv NT_TRANSACT_GET_USER_QUOTA\n"));
168                 goto cleanup;
169         }
170
171         if (cli_is_error(cli)) {
172                 ret = False;
173                 goto cleanup;
174         } else {
175                 ret = True;
176         }
177
178         if ((rparam&&rdata)&&(rparam_count>=4&&rdata_count>=8)) {
179                 ret = parse_user_quota_record(rdata, rdata_count, &offset, pqt);
180         } else {
181                 DEBUG(0,("Got INVALID NT_TRANSACT_GET_USER_QUOTA reply.\n"));
182                 ret = False; 
183         }
184
185  cleanup:
186         SAFE_FREE(rparam);
187         SAFE_FREE(rdata); 
188         return ret;
189 }
190
191 bool cli_set_user_quota(struct cli_state *cli, int quota_fnum, SMB_NTQUOTA_STRUCT *pqt)
192 {
193         bool ret = False;
194         uint16 setup;
195         char params[2];
196         char data[112];
197         char *rparam=NULL, *rdata=NULL;
198         unsigned int rparam_count=0, rdata_count=0;
199         unsigned int sid_len;   
200         memset(data,'\0',112);
201
202         if (!cli||!pqt) {
203                 smb_panic("cli_set_user_quota() called with NULL Pointer!");
204         }
205
206         setup = NT_TRANSACT_SET_USER_QUOTA;
207
208         SSVAL(params,0,quota_fnum);
209
210         sid_len = ndr_size_dom_sid(&pqt->sid, NULL, 0);
211         SIVAL(data,0,0);
212         SIVAL(data,4,sid_len);
213         SBIG_UINT(data, 8,(uint64_t)0);
214         SBIG_UINT(data,16,pqt->usedspace);
215         SBIG_UINT(data,24,pqt->softlim);
216         SBIG_UINT(data,32,pqt->hardlim);
217         sid_linearize(data+40, sid_len, &pqt->sid);
218
219         if (!cli_send_nt_trans(cli, 
220                                NT_TRANSACT_SET_USER_QUOTA, 
221                                0, 
222                                &setup, 1, 0,
223                                params, 2, 0,
224                                data, 112, 0)) {
225                 DEBUG(1,("Failed to send NT_TRANSACT_SET_USER_QUOTA\n"));
226                 goto cleanup;
227         }
228
229
230         if (!cli_receive_nt_trans(cli, 
231                                   &rparam, &rparam_count,
232                                   &rdata, &rdata_count)) {
233                 DEBUG(1,("NT_TRANSACT_SET_USER_QUOTA failed\n"));
234                 goto cleanup;
235         }
236
237         if (cli_is_error(cli)) {
238                 ret = False;
239                 goto cleanup;
240         } else {
241                 ret = True;
242         }
243
244   cleanup:
245         SAFE_FREE(rparam);
246         SAFE_FREE(rdata);
247         return ret;
248 }
249
250 bool cli_list_user_quota(struct cli_state *cli, int quota_fnum, SMB_NTQUOTA_LIST **pqt_list)
251 {
252         bool ret = False;
253         uint16 setup;
254         char params[16];
255         char *rparam=NULL, *rdata=NULL;
256         unsigned int rparam_count=0, rdata_count=0;
257         unsigned int offset;
258         const char *curdata = NULL;
259         unsigned int curdata_count = 0;
260         TALLOC_CTX *mem_ctx = NULL;
261         SMB_NTQUOTA_STRUCT qt;
262         SMB_NTQUOTA_LIST *tmp_list_ent;
263
264         if (!cli||!pqt_list) {
265                 smb_panic("cli_list_user_quota() called with NULL Pointer!");
266         }
267
268         setup = NT_TRANSACT_GET_USER_QUOTA;
269
270         SSVAL(params, 0,quota_fnum);
271         SSVAL(params, 2,TRANSACT_GET_USER_QUOTA_LIST_START);
272         SIVAL(params, 4,0x00000000);
273         SIVAL(params, 8,0x00000000);
274         SIVAL(params,12,0x00000000);
275
276         if (!cli_send_nt_trans(cli, 
277                                NT_TRANSACT_GET_USER_QUOTA, 
278                                0, 
279                                &setup, 1, 0,
280                                params, 16, 4,
281                                NULL, 0, 2048)) {
282                 DEBUG(1,("Failed to send NT_TRANSACT_GET_USER_QUOTA\n"));
283                 goto cleanup;
284         }
285
286
287         if (!cli_receive_nt_trans(cli,
288                                   &rparam, &rparam_count,
289                                   &rdata, &rdata_count)) {
290                 DEBUG(1,("Failed to recv NT_TRANSACT_GET_USER_QUOTA\n"));
291                 goto cleanup;
292         }
293
294         if (cli_is_error(cli)) {
295                 ret = False;
296                 goto cleanup;
297         } else {
298                 ret = True;
299         }
300
301         if (rdata_count == 0) {
302                 *pqt_list = NULL;
303                 return True;
304         }
305
306         if ((mem_ctx=talloc_init("SMB_USER_QUOTA_LIST"))==NULL) {
307                 DEBUG(0,("talloc_init() failed\n"));
308                 return (-1);
309         }
310
311         offset = 1;
312         for (curdata=rdata,curdata_count=rdata_count;
313                 ((curdata)&&(curdata_count>=8)&&(offset>0));
314                 curdata +=offset,curdata_count -= offset) {
315                 ZERO_STRUCT(qt);
316                 if (!parse_user_quota_record(curdata, curdata_count, &offset, &qt)) {
317                         DEBUG(1,("Failed to parse the quota record\n"));
318                         goto cleanup;
319                 }
320
321                 if ((tmp_list_ent=TALLOC_ZERO_P(mem_ctx,SMB_NTQUOTA_LIST))==NULL) {
322                         DEBUG(0,("TALLOC_ZERO() failed\n"));
323                         talloc_destroy(mem_ctx);
324                         return (-1);
325                 }
326
327                 if ((tmp_list_ent->quotas=TALLOC_ZERO_P(mem_ctx,SMB_NTQUOTA_STRUCT))==NULL) {
328                         DEBUG(0,("TALLOC_ZERO() failed\n"));
329                         talloc_destroy(mem_ctx);
330                         return (-1);
331                 }
332
333                 memcpy(tmp_list_ent->quotas,&qt,sizeof(qt));
334                 tmp_list_ent->mem_ctx = mem_ctx;                
335
336                 DLIST_ADD((*pqt_list),tmp_list_ent);
337         }
338
339         SSVAL(params, 2,TRANSACT_GET_USER_QUOTA_LIST_CONTINUE); 
340         while(1) {
341                 if (!cli_send_nt_trans(cli, 
342                                        NT_TRANSACT_GET_USER_QUOTA, 
343                                        0, 
344                                        &setup, 1, 0,
345                                        params, 16, 4,
346                                        NULL, 0, 2048)) {
347                         DEBUG(1,("Failed to send NT_TRANSACT_GET_USER_QUOTA\n"));
348                         goto cleanup;
349                 }
350
351                 SAFE_FREE(rparam);
352                 SAFE_FREE(rdata);
353                 if (!cli_receive_nt_trans(cli,
354                                           &rparam, &rparam_count,
355                                           &rdata, &rdata_count)) {
356                         DEBUG(1,("Failed to recv NT_TRANSACT_GET_USER_QUOTA\n"));
357                         goto cleanup;
358                 }
359
360                 if (cli_is_error(cli)) {
361                         ret = False;
362                         goto cleanup;
363                 } else {
364                         ret = True;
365                 }
366
367                 if (rdata_count == 0) {
368                         break;  
369                 }
370
371                 offset = 1;
372                 for (curdata=rdata,curdata_count=rdata_count;
373                         ((curdata)&&(curdata_count>=8)&&(offset>0));
374                         curdata +=offset,curdata_count -= offset) {
375                         ZERO_STRUCT(qt);
376                         if (!parse_user_quota_record(curdata, curdata_count, &offset, &qt)) {
377                                 DEBUG(1,("Failed to parse the quota record\n"));
378                                 goto cleanup;
379                         }
380
381                         if ((tmp_list_ent=TALLOC_ZERO_P(mem_ctx,SMB_NTQUOTA_LIST))==NULL) {
382                                 DEBUG(0,("TALLOC_ZERO() failed\n"));
383                                 talloc_destroy(mem_ctx);
384                                 goto cleanup;
385                         }
386
387                         if ((tmp_list_ent->quotas=TALLOC_ZERO_P(mem_ctx,SMB_NTQUOTA_STRUCT))==NULL) {
388                                 DEBUG(0,("TALLOC_ZERO() failed\n"));
389                                 talloc_destroy(mem_ctx);
390                                 goto cleanup;
391                         }
392
393                         memcpy(tmp_list_ent->quotas,&qt,sizeof(qt));
394                         tmp_list_ent->mem_ctx = mem_ctx;                
395
396                         DLIST_ADD((*pqt_list),tmp_list_ent);
397                 }
398         }
399
400
401         ret = True;
402  cleanup:
403         SAFE_FREE(rparam);
404         SAFE_FREE(rdata);
405
406         return ret;
407 }
408
409 bool cli_get_fs_quota_info(struct cli_state *cli, int quota_fnum, SMB_NTQUOTA_STRUCT *pqt)
410 {
411         bool ret = False;
412         uint16 setup;
413         char param[2];
414         char *rparam=NULL, *rdata=NULL;
415         unsigned int rparam_count=0, rdata_count=0;
416         SMB_NTQUOTA_STRUCT qt;
417         ZERO_STRUCT(qt);
418
419         if (!cli||!pqt) {
420                 smb_panic("cli_get_fs_quota_info() called with NULL Pointer!");
421         }
422
423         setup = TRANSACT2_QFSINFO;
424
425         SSVAL(param,0,SMB_FS_QUOTA_INFORMATION);
426
427         if (!cli_send_trans(cli, SMBtrans2, 
428                     NULL, 
429                     0, 0,
430                     &setup, 1, 0,
431                     param, 2, 0,
432                     NULL, 0, 560)) {
433                 goto cleanup;
434         }
435
436         if (!cli_receive_trans(cli, SMBtrans2,
437                               &rparam, &rparam_count,
438                               &rdata, &rdata_count)) {
439                 goto cleanup;
440         }
441
442         if (cli_is_error(cli)) {
443                 ret = False;
444                 goto cleanup;
445         } else {
446                 ret = True;
447         }
448
449         if (rdata_count < 48) {
450                 goto cleanup;
451         }
452
453         /* unknown_1 24 NULL bytes in pdata*/
454
455         /* the soft quotas 8 bytes (uint64_t)*/
456         qt.softlim = (uint64_t)IVAL(rdata,24);
457 #ifdef LARGE_SMB_OFF_T
458         qt.softlim |= (((uint64_t)IVAL(rdata,28)) << 32);
459 #else /* LARGE_SMB_OFF_T */
460         if ((IVAL(rdata,28) != 0)&&
461                 ((qt.softlim != 0xFFFFFFFF)||
462                  (IVAL(rdata,28)!=0xFFFFFFFF))) {
463                 /* more than 32 bits? */
464                 goto cleanup;
465         }
466 #endif /* LARGE_SMB_OFF_T */
467
468         /* the hard quotas 8 bytes (uint64_t)*/
469         qt.hardlim = (uint64_t)IVAL(rdata,32);
470 #ifdef LARGE_SMB_OFF_T
471         qt.hardlim |= (((uint64_t)IVAL(rdata,36)) << 32);
472 #else /* LARGE_SMB_OFF_T */
473         if ((IVAL(rdata,36) != 0)&&
474                 ((qt.hardlim != 0xFFFFFFFF)||
475                  (IVAL(rdata,36)!=0xFFFFFFFF))) {
476                 /* more than 32 bits? */
477                 goto cleanup;
478         }
479 #endif /* LARGE_SMB_OFF_T */
480
481         /* quota_flags 2 bytes **/
482         qt.qflags = SVAL(rdata,40);
483
484         qt.qtype = SMB_USER_FS_QUOTA_TYPE;
485
486         *pqt = qt;
487
488         ret = True;
489 cleanup:
490         SAFE_FREE(rparam);
491         SAFE_FREE(rdata);
492
493         return ret;     
494 }
495
496 bool cli_set_fs_quota_info(struct cli_state *cli, int quota_fnum, SMB_NTQUOTA_STRUCT *pqt)
497 {
498         bool ret = False;
499         uint16 setup;
500         char param[4];
501         char data[48];
502         char *rparam=NULL, *rdata=NULL;
503         unsigned int rparam_count=0, rdata_count=0;
504         SMB_NTQUOTA_STRUCT qt;
505         ZERO_STRUCT(qt);
506         memset(data,'\0',48);
507
508         if (!cli||!pqt) {
509                 smb_panic("cli_set_fs_quota_info() called with NULL Pointer!");
510         }
511
512         setup = TRANSACT2_SETFSINFO;
513
514         SSVAL(param,0,quota_fnum);
515         SSVAL(param,2,SMB_FS_QUOTA_INFORMATION);
516
517         /* Unknown1 24 NULL bytes*/
518
519         /* Default Soft Quota 8 bytes */
520         SBIG_UINT(data,24,pqt->softlim);
521
522         /* Default Hard Quota 8 bytes */
523         SBIG_UINT(data,32,pqt->hardlim);
524
525         /* Quota flag 2 bytes */
526         SSVAL(data,40,pqt->qflags);
527
528         /* Unknown3 6 NULL bytes */
529
530         if (!cli_send_trans(cli, SMBtrans2, 
531                     NULL, 
532                     0, 0,
533                     &setup, 1, 0,
534                     param, 4, 0,
535                     data, 48, 0)) {
536                 goto cleanup;
537         }
538
539         if (!cli_receive_trans(cli, SMBtrans2,
540                               &rparam, &rparam_count,
541                               &rdata, &rdata_count)) {
542                 goto cleanup;
543         }
544
545         if (cli_is_error(cli)) {
546                 ret = False;
547                 goto cleanup;
548         } else {
549                 ret = True;
550         }
551
552 cleanup:
553         SAFE_FREE(rparam);
554         SAFE_FREE(rdata);
555
556         return ret;     
557 }
558
559 static const char *quota_str_static(uint64_t val, bool special, bool _numeric)
560 {
561         const char *result;
562
563         if (!_numeric&&special&&(val == SMB_NTQUOTAS_NO_LIMIT)) {
564                 return "NO LIMIT";
565         }
566         result = talloc_asprintf(talloc_tos(), "%"PRIu64, val);
567         SMB_ASSERT(result != NULL);
568         return result;
569 }
570
571 void dump_ntquota(SMB_NTQUOTA_STRUCT *qt, bool _verbose, bool _numeric, void (*_sidtostring)(fstring str, DOM_SID *sid, bool _numeric))
572 {
573         TALLOC_CTX *frame = talloc_stackframe();
574
575         if (!qt) {
576                 smb_panic("dump_ntquota() called with NULL pointer");
577         }
578
579         switch (qt->qtype) {
580                 case SMB_USER_FS_QUOTA_TYPE:
581                         {
582                                 d_printf("File System QUOTAS:\n");
583                                 d_printf("Limits:\n");
584                                 d_printf(" Default Soft Limit: %15s\n",quota_str_static(qt->softlim,True,_numeric));
585                                 d_printf(" Default Hard Limit: %15s\n",quota_str_static(qt->hardlim,True,_numeric));
586                                 d_printf("Quota Flags:\n");
587                                 d_printf(" Quotas Enabled: %s\n",
588                                         ((qt->qflags&QUOTAS_ENABLED)||(qt->qflags&QUOTAS_DENY_DISK))?"On":"Off");
589                                 d_printf(" Deny Disk:      %s\n",(qt->qflags&QUOTAS_DENY_DISK)?"On":"Off");
590                                 d_printf(" Log Soft Limit: %s\n",(qt->qflags&QUOTAS_LOG_THRESHOLD)?"On":"Off");
591                                 d_printf(" Log Hard Limit: %s\n",(qt->qflags&QUOTAS_LOG_LIMIT)?"On":"Off");
592                         }
593                         break;
594                 case SMB_USER_QUOTA_TYPE:
595                         {
596                                 fstring username_str = {0};
597
598                                 if (_sidtostring) {
599                                         _sidtostring(username_str,&qt->sid,_numeric);
600                                 } else {
601                                         sid_to_fstring(username_str, &qt->sid);
602                                 }
603
604                                 if (_verbose) { 
605                                         d_printf("Quotas for User: %s\n",username_str);
606                                         d_printf("Used Space: %15s\n",quota_str_static(qt->usedspace,False,_numeric));
607                                         d_printf("Soft Limit: %15s\n",quota_str_static(qt->softlim,True,_numeric));
608                                         d_printf("Hard Limit: %15s\n",quota_str_static(qt->hardlim,True,_numeric));
609                                 } else {
610                                         d_printf("%-30s: ",username_str);
611                                         d_printf("%15s/",quota_str_static(qt->usedspace,False,_numeric));
612                                         d_printf("%15s/",quota_str_static(qt->softlim,True,_numeric));
613                                         d_printf("%15s\n",quota_str_static(qt->hardlim,True,_numeric));
614                                 }
615                         }
616                         break;
617                 default:
618                         d_printf("dump_ntquota() invalid qtype(%d)\n",qt->qtype);
619         }
620         TALLOC_FREE(frame);
621         return;
622 }
623
624 void dump_ntquota_list(SMB_NTQUOTA_LIST **qtl, bool _verbose, bool _numeric, void (*_sidtostring)(fstring str, DOM_SID *sid, bool _numeric))
625 {
626         SMB_NTQUOTA_LIST *cur;
627
628         for (cur = *qtl;cur;cur = cur->next) {
629                 if (cur->quotas)
630                         dump_ntquota(cur->quotas,_verbose,_numeric,_sidtostring);
631         }       
632 }