First set of speed improvements from Ying Chen <ying@almaden.ibm.com>.
[ira/wip.git] / source3 / smbd / blocking.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    Blocking Locking functions
5    Copyright (C) Jeremy Allison 1998
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23 extern int DEBUGLEVEL;
24 extern int Client;
25 extern char *OutBuffer;
26
27 /****************************************************************************
28  This is the structure to queue to implement blocking locks.
29  notify. It consists of the requesting SMB and the expiry time.
30 *****************************************************************************/
31
32 typedef struct {
33   ubi_slNode msg_next;
34   int com_type;
35   files_struct *fsp;
36   time_t expire_time;
37   int lock_num;
38   char *inbuf;
39   int length;
40 } blocking_lock_record;
41
42 static ubi_slList blocking_lock_queue = { NULL, (ubi_slNodePtr)&blocking_lock_queue, 0};
43
44 /****************************************************************************
45  Destructor for the above structure.
46 ****************************************************************************/
47
48 static void free_blocking_lock_record(blocking_lock_record *blr)
49 {
50   free(blr->inbuf);
51   free((char *)blr);
52 }
53
54 /****************************************************************************
55  Get the files_struct given a particular queued SMB.
56 *****************************************************************************/
57
58 static files_struct *get_fsp_from_pkt(char *inbuf)
59 {
60   switch(CVAL(inbuf,smb_com)) {
61   case SMBlock:
62   case SMBlockread:
63     return file_fsp(inbuf,smb_vwv0);
64   case SMBlockingX:
65     return file_fsp(inbuf,smb_vwv2);
66   default:
67     DEBUG(0,("get_fsp_from_pkt: PANIC - unknown type on blocking lock queue - exiting.!\n"));
68     exit_server("PANIC - unknown type on blocking lock queue");
69   }
70   return NULL; /* Keep compiler happy. */
71 }
72
73 /****************************************************************************
74  Determine if this is a secondary element of a chained SMB.
75   **************************************************************************/
76
77 static BOOL in_chained_smb(void)
78 {
79   return (chain_size != 0);
80 }
81
82 /****************************************************************************
83  Function to push a blocking lock request onto the lock queue.
84 ****************************************************************************/
85
86 BOOL push_blocking_lock_request( char *inbuf, int length, int lock_timeout, int lock_num)
87 {
88   blocking_lock_record *blr;
89
90   if(in_chained_smb() ) {
91     DEBUG(0,("push_blocking_lock_request: cannot queue a chained request (currently).\n"));
92     return False;
93   }
94
95   /*
96    * Now queue an entry on the blocking lock queue. We setup
97    * the expiration time here.
98    */
99
100   if((blr = (blocking_lock_record *)malloc(sizeof(blocking_lock_record))) == NULL) {
101     DEBUG(0,("push_blocking_lock_request: Malloc fail !\n" ));
102     return False;
103   }
104
105   if((blr->inbuf = (char *)malloc(length)) == NULL) {
106     DEBUG(0,("push_blocking_lock_request: Malloc fail (2)!\n" ));
107     free((char *)blr);
108     return False;
109   }
110
111   blr->com_type = CVAL(inbuf,smb_com);
112   blr->fsp = get_fsp_from_pkt(inbuf);
113   blr->expire_time = (lock_timeout == -1) ? (time_t)-1 : time(NULL) + (time_t)lock_timeout;
114   blr->lock_num = lock_num;
115   memcpy(blr->inbuf, inbuf, length);
116   blr->length = length;
117
118   ubi_slAddTail(&blocking_lock_queue, blr);
119
120
121   DEBUG(3,("push_blocking_lock_request: lock request length=%d blocked with expiry time %d \
122 for fnum = %d, name = %s\n", length, (int)blr->expire_time, 
123         blr->fsp->fnum, blr->fsp->fsp_name ));
124
125   return True;
126 }
127
128 /****************************************************************************
129  Return a smd with a given size.
130 *****************************************************************************/
131
132 static void send_blocking_reply(char *outbuf, int outsize)
133 {
134   if(outsize > 4)
135     smb_setlen(outbuf,outsize - 4);
136
137   send_smb(Client,outbuf);
138 }
139
140 /****************************************************************************
141  Return a lockingX success SMB.
142 *****************************************************************************/
143
144 static void reply_lockingX_success(blocking_lock_record *blr)
145 {
146   char *outbuf = OutBuffer;
147   int bufsize = BUFFER_SIZE;
148   char *inbuf = blr->inbuf;
149   int outsize = 0;
150
151   construct_reply_common(inbuf, outbuf);
152   set_message(outbuf,2,0,True);
153
154   /*
155    * As this message is a lockingX call we must handle
156    * any following chained message correctly.
157    * This is normally handled in construct_reply(),
158    * but as that calls switch_message, we can't use
159    * that here and must set up the chain info manually.
160    */
161
162   outsize = chain_reply(inbuf,outbuf,blr->length,bufsize);
163
164   outsize += chain_size;
165
166   send_blocking_reply(outbuf,outsize);
167 }
168
169 /****************************************************************************
170  Return a generic lock fail error blocking call.
171 *****************************************************************************/
172
173 static void generic_blocking_lock_error(blocking_lock_record *blr, int eclass, int32 ecode)
174 {
175   char *outbuf = OutBuffer;
176   char *inbuf = blr->inbuf;
177   construct_reply_common(inbuf, outbuf);
178
179   if(eclass == 0) /* NT Error. */
180     SSVAL(outbuf,smb_flg2, SVAL(outbuf,smb_flg2) | FLAGS2_32_BIT_ERROR_CODES);
181
182   ERROR(eclass,ecode);
183   send_smb(Client,outbuf);
184 }
185
186 /****************************************************************************
187  Return a lock fail error for a lockingX call. Undo all the locks we have 
188  obtained first.
189 *****************************************************************************/
190
191 static void reply_lockingX_error(blocking_lock_record *blr, int eclass, int32 ecode)
192 {
193   char *inbuf = blr->inbuf;
194   files_struct *fsp = blr->fsp;
195   connection_struct *conn = conn_find(SVAL(inbuf,smb_tid));
196   uint16 num_ulocks = SVAL(inbuf,smb_vwv6);
197   SMB_OFF_T count = (SMB_OFF_T) 0, offset = (SMB_OFF_T) 0;
198   unsigned char locktype = CVAL(inbuf,smb_vwv3);
199   BOOL large_file_format = (locktype & LOCKING_ANDX_LARGE_FILES);
200   char *data;
201   int i;
202
203   data = smb_buf(inbuf) + ((large_file_format ? 20 : 10)*num_ulocks);
204
205   /* 
206    * Data now points at the beginning of the list
207    * of smb_lkrng structs.
208    */
209
210   /*
211    * Ensure we don't do a remove on the lock that just failed,
212    * as under POSIX rules, if we have a lock already there, we
213    * will delete it (and we shouldn't) .....
214    */
215
216   for(i = blr->lock_num - 1; i >= 0; i--) {
217     int dummy1;
218     uint32 dummy2;
219     BOOL err;
220
221     count = get_lock_count( data, i, large_file_format, &err);
222     offset = get_lock_offset( data, i, large_file_format, &err);
223
224     /*
225      * We know err cannot be set as if it was the lock
226      * request would never have been queued. JRA.
227      */
228
229     do_unlock(fsp,conn,count,offset,&dummy1,&dummy2);
230   }
231
232   generic_blocking_lock_error(blr, eclass, ecode);
233 }
234
235 /****************************************************************************
236  Return a lock fail error.
237 *****************************************************************************/
238
239 static void blocking_lock_reply_error(blocking_lock_record *blr, int eclass, int32 ecode)
240 {
241   switch(blr->com_type) {
242   case SMBlock:
243     generic_blocking_lock_error(blr, eclass, ecode);
244     break;
245   case SMBlockread:
246     generic_blocking_lock_error(blr, eclass, ecode);
247     break;
248   case SMBlockingX:
249     reply_lockingX_error(blr, eclass, ecode);
250     break;
251   default:
252     DEBUG(0,("blocking_lock_reply_error: PANIC - unknown type on blocking lock queue - exiting.!\n"));
253     exit_server("PANIC - unknown type on blocking lock queue");
254   }
255 }
256
257 /****************************************************************************
258  Attempt to finish off getting all pending blocking locks for a lockread call.
259  Returns True if we want to be removed from the list.
260 *****************************************************************************/
261
262 static BOOL process_lockread(blocking_lock_record *blr)
263 {
264   char *outbuf = OutBuffer;
265   char *inbuf = blr->inbuf;
266   ssize_t nread = -1;
267   char *data;
268   int outsize = 0;
269   SMB_OFF_T startpos;
270   size_t numtoread;
271   int eclass;
272   uint32 ecode;
273   connection_struct *conn = conn_find(SVAL(inbuf,smb_tid));
274   files_struct *fsp = blr->fsp;
275
276   numtoread = SVAL(inbuf,smb_vwv1);
277   startpos = IVAL(inbuf,smb_vwv2);
278  
279   numtoread = MIN(BUFFER_SIZE-outsize,numtoread);
280   data = smb_buf(outbuf) + 3;
281  
282   if(!do_lock( fsp, conn, numtoread, startpos, READ_LOCK, &eclass, &ecode)) {
283     if((errno != EACCES) && (errno != EAGAIN)) {
284       /*
285        * We have other than a "can't get lock" POSIX
286        * error. Send an error.
287        * Return True so we get dequeued.
288        */
289
290       generic_blocking_lock_error(blr, eclass, ecode);
291       return True;
292     }
293
294     /*
295      * Still waiting for lock....
296      */
297
298     DEBUG(10,("process_lockread: failed to get lock for file = %s. Still waiting....\n",
299           fsp->fsp_name));
300     return False;
301   }
302
303   nread = read_file(fsp,data,startpos,numtoread);
304
305   if (nread < 0) {
306     generic_blocking_lock_error(blr,ERRDOS,ERRnoaccess);
307     return True;
308   }
309
310   construct_reply_common(inbuf, outbuf);
311   outsize = set_message(outbuf,5,3,True);
312
313   outsize += nread;
314   SSVAL(outbuf,smb_vwv0,nread);
315   SSVAL(outbuf,smb_vwv5,nread+3);
316   SSVAL(smb_buf(outbuf),1,nread);
317
318   DEBUG(3, ( "process_lockread file = %s, fnum=%d num=%d nread=%d\n",
319         fsp->fsp_name, fsp->fnum, (int)numtoread, (int)nread ) );
320
321   send_blocking_reply(outbuf,outsize);
322   return True;
323 }
324
325 /****************************************************************************
326  Attempt to finish off getting all pending blocking locks for a lock call.
327  Returns True if we want to be removed from the list.
328 *****************************************************************************/
329
330 static BOOL process_lock(blocking_lock_record *blr)
331 {
332   char *outbuf = OutBuffer;
333   char *inbuf = blr->inbuf;
334   int outsize;
335   SMB_OFF_T count = 0, offset = 0;
336   int eclass;
337   uint32 ecode;
338   connection_struct *conn = conn_find(SVAL(inbuf,smb_tid));
339   files_struct *fsp = blr->fsp;
340
341   count = IVAL(inbuf,smb_vwv1);
342   offset = IVAL(inbuf,smb_vwv3);
343
344   errno = 0;
345   if (!do_lock(fsp, conn, count, offset, WRITE_LOCK, &eclass, &ecode)) {
346     if((errno != EACCES) && (errno != EAGAIN)) {
347
348       /*
349        * We have other than a "can't get lock" POSIX
350        * error. Send an error.
351        * Return True so we get dequeued.
352        */
353
354       blocking_lock_reply_error(blr, eclass, ecode);
355       return True;
356     }
357
358     /*
359      * Still can't get the lock - keep waiting.
360      */
361
362     DEBUG(10,("process_lock: failed to get lock for file = %s. Still waiting....\n",
363           fsp->fsp_name));
364     return False;
365   }
366
367   /*
368    * Success - we got the lock.
369    */
370
371   DEBUG(3,("process_lock : file=%s fnum=%d offset=%.0f count=%.0f\n",
372        fsp->fsp_name, fsp->fnum, (double)offset, (double)count));
373
374   construct_reply_common(inbuf, outbuf);
375   outsize = set_message(outbuf,0,0,True);
376   send_blocking_reply(outbuf,outsize);
377   return True;
378 }
379
380 /****************************************************************************
381  Attempt to finish off getting all pending blocking locks for a lockingX call.
382  Returns True if we want to be removed from the list.
383 *****************************************************************************/
384
385 static BOOL process_lockingX(blocking_lock_record *blr)
386 {
387   char *inbuf = blr->inbuf;
388   unsigned char locktype = CVAL(inbuf,smb_vwv3);
389   files_struct *fsp = blr->fsp;
390   connection_struct *conn = conn_find(SVAL(inbuf,smb_tid));
391   uint16 num_ulocks = SVAL(inbuf,smb_vwv6);
392   uint16 num_locks = SVAL(inbuf,smb_vwv7);
393   SMB_OFF_T count = 0, offset = 0;
394   BOOL large_file_format = (locktype & LOCKING_ANDX_LARGE_FILES);
395   char *data;
396   int eclass=0;
397   uint32 ecode=0;
398
399   data = smb_buf(inbuf) + ((large_file_format ? 20 : 10)*num_ulocks);
400
401   /* 
402    * Data now points at the beginning of the list
403    * of smb_lkrng structs.
404    */
405
406   for(; blr->lock_num < num_locks; blr->lock_num++) {
407     BOOL err;
408
409     count = get_lock_count( data, blr->lock_num, large_file_format, &err);
410     offset = get_lock_offset( data, blr->lock_num, large_file_format, &err);
411
412     /*
413      * We know err cannot be set as if it was the lock
414      * request would never have been queued. JRA.
415      */
416     errno = 0;
417     if(!do_lock(fsp,conn,count,offset, ((locktype & 1) ? READ_LOCK : WRITE_LOCK),
418                 &eclass, &ecode))
419       break;
420   }
421
422   if(blr->lock_num == num_locks) {
423
424     /*
425      * Success - we got all the locks.
426      */
427
428     DEBUG(3,("process_lockingX file = %s, fnum=%d type=%d num_locks=%d\n",
429           fsp->fsp_name, fsp->fnum, (unsigned int)locktype, num_locks) );
430
431     reply_lockingX_success(blr);
432     return True;
433
434   } else if((errno != EACCES) && (errno != EAGAIN)) {
435
436     /*
437      * We have other than a "can't get lock" POSIX
438      * error. Free any locks we had and return an error.
439      * Return True so we get dequeued.
440      */
441
442     blocking_lock_reply_error(blr, eclass, ecode);
443     return True;
444   }
445
446   /*
447    * Still can't get all the locks - keep waiting.
448    */
449
450   DEBUG(10,("process_lockingX: only got %d locks of %d needed for file %s, fnum = %d. \
451 Waiting....\n", blr->lock_num, num_locks, fsp->fsp_name, fsp->fnum));
452
453   return False;
454 }
455
456 /****************************************************************************
457  Process a blocking lock SMB.
458  Returns True if we want to be removed from the list.
459 *****************************************************************************/
460
461 static BOOL blocking_lock_record_process(blocking_lock_record *blr)
462 {
463   switch(blr->com_type) {
464   case SMBlock:
465     return process_lock(blr);
466   case SMBlockread:
467     return process_lockread(blr);
468   case SMBlockingX:
469     return process_lockingX(blr);
470   default:
471     DEBUG(0,("blocking_lock_record_process: PANIC - unknown type on blocking lock queue - exiting.!\n"));
472     exit_server("PANIC - unknown type on blocking lock queue");
473   }
474   return False; /* Keep compiler happy. */
475 }
476
477 /****************************************************************************
478  Delete entries by fnum from the blocking lock pending queue.
479 *****************************************************************************/
480
481 void remove_pending_lock_requests_by_fid(files_struct *fsp)
482 {
483   blocking_lock_record *blr = (blocking_lock_record *)ubi_slFirst( &blocking_lock_queue );
484   blocking_lock_record *prev = NULL;
485
486   while(blr != NULL) {
487     if(blr->fsp->fnum == fsp->fnum) {
488
489       DEBUG(10,("remove_pending_lock_requests_by_fid - removing request type %d for \
490 file %s fnum = %d\n", blr->com_type, fsp->fsp_name, fsp->fnum ));
491
492       free_blocking_lock_record((blocking_lock_record *)ubi_slRemNext( &blocking_lock_queue, prev));
493       blr = (blocking_lock_record *)(prev ? ubi_slNext(prev) : ubi_slFirst(&blocking_lock_queue));
494       continue;
495     }
496
497     prev = blr;
498     blr = (blocking_lock_record *)ubi_slNext(blr);
499   }
500 }
501
502 /****************************************************************************
503  Delete entries by mid from the blocking lock pending queue. Always send reply.
504 *****************************************************************************/
505
506 void remove_pending_lock_requests_by_mid(int mid)
507 {
508   blocking_lock_record *blr = (blocking_lock_record *)ubi_slFirst( &blocking_lock_queue );
509   blocking_lock_record *prev = NULL;
510
511   while(blr != NULL) {
512     if(SVAL(blr->inbuf,smb_mid) == mid) {
513       files_struct *fsp = blr->fsp;
514
515       DEBUG(10,("remove_pending_lock_requests_by_mid - removing request type %d for \
516 file %s fnum = %d\n", blr->com_type, fsp->fsp_name, fsp->fnum ));
517
518       blocking_lock_reply_error(blr,0,NT_STATUS_CANCELLED);
519       free_blocking_lock_record((blocking_lock_record *)ubi_slRemNext( &blocking_lock_queue, prev));
520       blr = (blocking_lock_record *)(prev ? ubi_slNext(prev) : ubi_slFirst(&blocking_lock_queue));
521       continue;
522     }
523
524     prev = blr;
525     blr = (blocking_lock_record *)ubi_slNext(blr);
526   }
527 }
528
529 /****************************************************************************
530  Return True if the blocking lock queue has entries.
531 *****************************************************************************/
532
533 BOOL blocking_locks_pending(void)
534 {
535   blocking_lock_record *blr = (blocking_lock_record *)ubi_slFirst( &blocking_lock_queue );
536   return (blr == NULL ? False : True);
537 }
538
539 /****************************************************************************
540  Process the blocking lock queue. Note that this is only called as root.
541 *****************************************************************************/
542
543 void process_blocking_lock_queue(time_t t)
544 {
545   blocking_lock_record *blr = (blocking_lock_record *)ubi_slFirst( &blocking_lock_queue );
546   blocking_lock_record *prev = NULL;
547
548   if(blr == NULL)
549     return;
550
551   /*
552    * Go through the queue and see if we can get any of the locks.
553    */
554
555   while(blr != NULL) {
556     connection_struct *conn = NULL;
557     uint16 vuid;
558     files_struct *fsp = NULL;
559
560     /*
561      * Ensure we don't have any old chain_fsp values
562      * sitting around....
563      */
564     chain_size = 0;
565     file_chain_reset();
566     fsp = blr->fsp;
567
568     conn = conn_find(SVAL(blr->inbuf,smb_tid));
569     vuid = (lp_security() == SEC_SHARE) ? UID_FIELD_INVALID :
570                   SVAL(blr->inbuf,smb_uid);
571
572     DEBUG(5,("process_blocking_lock_queue: examining pending lock fnum = %d for file %s\n",
573           fsp->fnum, fsp->fsp_name ));
574
575     if((blr->expire_time != -1) && (blr->expire_time > t)) {
576       /*
577        * Lock expired - throw away all previously
578        * obtained locks and return lock error.
579        */
580       DEBUG(5,("process_blocking_lock_queue: pending lock fnum = %d for file %s timed out.\n",
581           fsp->fnum, fsp->fsp_name ));
582
583       blocking_lock_reply_error(blr,ERRSRV,ERRaccess);
584       free_blocking_lock_record((blocking_lock_record *)ubi_slRemNext( &blocking_lock_queue, prev));
585       blr = (blocking_lock_record *)(prev ? ubi_slNext(prev) : ubi_slFirst(&blocking_lock_queue));
586       continue;
587     }
588
589     if(!become_user(conn,vuid)) {
590       DEBUG(0,("process_blocking_lock_queue: Unable to become user vuid=%d.\n",
591             vuid ));
592       /*
593        * Remove the entry and return an error to the client.
594        */
595       blocking_lock_reply_error(blr,ERRSRV,ERRaccess);
596       free_blocking_lock_record((blocking_lock_record *)ubi_slRemNext( &blocking_lock_queue, prev));
597       blr = (blocking_lock_record *)(prev ? ubi_slNext(prev) : ubi_slFirst(&blocking_lock_queue));
598       continue;
599     }
600
601     if(!become_service(conn,True)) {
602       DEBUG(0,("process_blocking_lock_queue: Unable to become service Error was %s.\n", strerror(errno) ));
603       /*
604        * Remove the entry and return an error to the client.
605        */
606       blocking_lock_reply_error(blr,ERRSRV,ERRaccess);
607       free_blocking_lock_record((blocking_lock_record *)ubi_slRemNext( &blocking_lock_queue, prev));
608       blr = (blocking_lock_record *)(prev ? ubi_slNext(prev) : ubi_slFirst(&blocking_lock_queue));
609       unbecome_user();
610       continue;
611     }
612
613     /*
614      * Go through the remaining locks and try and obtain them.
615      * The call returns True if all locks were obtained successfully
616      * and False if we still need to wait.
617      */
618
619     if(blocking_lock_record_process(blr)) {
620       free_blocking_lock_record((blocking_lock_record *)ubi_slRemNext( &blocking_lock_queue, prev));
621       blr = (blocking_lock_record *)(prev ? ubi_slNext(prev) : ubi_slFirst(&blocking_lock_queue));
622       unbecome_user();
623       continue;
624     }
625
626     unbecome_user();
627
628     /*
629      * Move to the next in the list.
630      */
631     prev = blr;
632     blr = (blocking_lock_record *)ubi_slNext(blr);
633   }
634 }