Found and fixed the logic bug in write cache code. Amazingly helpful
[sfrench/samba-autobuild/.git] / source / smbd / fileio.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    read/write to a files_struct
5    Copyright (C) Andrew Tridgell 1992-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
24 static BOOL setup_write_cache(files_struct *, SMB_OFF_T);
25
26 /****************************************************************************
27 seek a file. Try to avoid the seek if possible
28 ****************************************************************************/
29
30 SMB_OFF_T seek_file(files_struct *fsp,SMB_OFF_T pos)
31 {
32   SMB_OFF_T offset = 0;
33   SMB_OFF_T seek_ret;
34
35   if (fsp->print_file && lp_postscript(fsp->conn->service))
36     offset = 3;
37
38   seek_ret = fsp->conn->vfs_ops.lseek(fsp,fsp->fd,pos+offset,SEEK_SET);
39
40   /*
41    * We want to maintain the fiction that we can seek
42    * on a fifo for file system purposes. This allows 
43    * people to set up UNIX fifo's that feed data to Windows
44    * applications. JRA.
45    */
46
47   if((seek_ret == -1) && (errno == ESPIPE)) {
48     seek_ret = pos+offset;
49     errno = 0;
50   }
51
52   if((seek_ret == -1) || (seek_ret != pos+offset)) {
53     DEBUG(0,("seek_file: sys_lseek failed. Error was %s\n", strerror(errno) ));
54     fsp->pos = -1;
55     return -1;
56   }
57
58   fsp->pos = seek_ret - offset;
59
60   DEBUG(10,("seek_file: requested pos = %.0f, new pos = %.0f\n",
61         (double)(pos+offset), (double)fsp->pos ));
62
63   return(fsp->pos);
64 }
65
66 /****************************************************************************
67  Read from write cache if we can.
68 ****************************************************************************/
69
70
71 BOOL read_from_write_cache(files_struct *fsp,char *data,SMB_OFF_T pos,size_t n)
72 {
73   write_cache *wcp = fsp->wcp;
74
75   if(!wcp)
76     return False;
77
78   if(n > wcp->data_size || pos < wcp->offset || pos + n > wcp->offset + wcp->data_size)
79     return False;
80
81   memcpy(data, wcp->data + (pos - wcp->offset), n);
82
83   DO_PROFILE_INC(writecache_read_hits);
84
85   return True;
86 }
87
88 /****************************************************************************
89 read from a file
90 ****************************************************************************/
91
92 ssize_t read_file(files_struct *fsp,char *data,SMB_OFF_T pos,size_t n)
93 {
94         ssize_t ret=0,readret;
95
96         /* you can't read from print files */
97         if (fsp->print_file)
98                 return -1;
99
100         /*
101          * Serve from write cache if we can.
102          */
103
104         if(read_from_write_cache(fsp, data, pos, n))
105                 return n;
106
107         flush_write_cache(fsp, READ_FLUSH);
108
109         if (seek_file(fsp,pos) == -1) {
110                 DEBUG(3,("read_file: Failed to seek to %.0f\n",(double)pos));
111                 return(ret);
112         }
113   
114         if (n > 0) {
115 #ifdef DMF_FIX
116                 int numretries = 3;
117 tryagain:
118                 readret = fsp->conn->vfs_ops.read(fsp,fsp->fd,data,n);
119                 if (readret == -1) {
120                         if ((errno == EAGAIN) && numretries) {
121                                 DEBUG(3,("read_file EAGAIN retry in 10 seconds\n"));
122                                 (void)sleep(10);
123                                 --numretries;
124                                 goto tryagain;
125                         }
126                         return -1;
127                 }
128 #else /* NO DMF fix. */
129                 readret = fsp->conn->vfs_ops.read(fsp,fsp->fd,data,n);
130                 if (readret == -1)
131                         return -1;
132 #endif
133                 if (readret > 0)
134                         ret += readret;
135         }
136
137         return(ret);
138 }
139
140 /* how many write cache buffers have been allocated */
141 static unsigned int allocated_write_caches;
142
143 /****************************************************************************
144  *Really* write to a file.
145 ****************************************************************************/
146
147 static ssize_t real_write_file(files_struct *fsp,char *data,SMB_OFF_T pos, size_t n)
148 {
149   if ((pos != -1) && (seek_file(fsp,pos) == -1))
150     return -1;
151
152   return vfs_write_data(fsp,data,n);
153 }
154
155 /****************************************************************************
156 write to a file
157 ****************************************************************************/
158
159 ssize_t write_file(files_struct *fsp, char *data, SMB_OFF_T pos, size_t n)
160 {
161   write_cache *wcp = fsp->wcp;
162   ssize_t total_written = 0;
163   int write_path = -1; 
164
165   if (fsp->print_file) {
166           return print_job_write(fsp->print_jobid, data, n);
167   }
168
169   if (!fsp->can_write) {
170     errno = EPERM;
171     return(0);
172   }
173
174   if (!fsp->modified) {
175     SMB_STRUCT_STAT st;
176     fsp->modified = True;
177
178     if (fsp->conn->vfs_ops.fstat(fsp,fsp->fd,&st) == 0) {
179       int dosmode = dos_mode(fsp->conn,fsp->fsp_name,&st);
180       if (MAP_ARCHIVE(fsp->conn) && !IS_DOS_ARCHIVE(dosmode)) { 
181         file_chmod(fsp->conn,fsp->fsp_name,dosmode | aARCH,&st);
182       }
183
184       /*
185        * If this is the first write and we have an exclusive oplock then setup
186        * the write cache.
187        */
188
189       if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type) && !wcp) {
190         setup_write_cache(fsp, st.st_size);
191         wcp = fsp->wcp;
192       } 
193     }  
194   }
195
196 #ifdef WITH_PROFILE
197   DO_PROFILE_INC(writecache_total_writes);
198   if (!fsp->oplock_type) {
199     DO_PROFILE_INC(writecache_non_oplock_writes);
200   }
201 #endif
202
203   /*
204    * If this file is level II oplocked then we need
205    * to grab the shared memory lock and inform all
206    * other files with a level II lock that they need
207    * to flush their read caches. We keep the lock over
208    * the shared memory area whilst doing this.
209    */
210
211   release_level_2_oplocks_on_change(fsp);
212
213 #ifdef WITH_PROFILE
214   if (profile_p && profile_p->writecache_total_writes % 500 == 0) {
215     DEBUG(3,("WRITECACHE: initwrites=%u abutted=%u total=%u \
216 nonop=%u allocated=%u active=%u direct=%u perfect=%u readhits=%u\n",
217         profile_p->writecache_init_writes,
218         profile_p->writecache_abutted_writes,
219         profile_p->writecache_total_writes,
220         profile_p->writecache_non_oplock_writes,
221         profile_p->writecache_allocated_write_caches,
222         profile_p->writecache_num_write_caches,
223         profile_p->writecache_direct_writes,
224         profile_p->writecache_num_perfect_writes,
225         profile_p->writecache_read_hits ));
226
227     DEBUG(3,("WRITECACHE: Flushes SEEK=%d, READ=%d, WRITE=%d, READRAW=%d, OPLOCK=%d, CLOSE=%d, SYNC=%d\n",
228         profile_p->writecache_flushed_writes[SEEK_FLUSH],
229         profile_p->writecache_flushed_writes[READ_FLUSH],
230         profile_p->writecache_flushed_writes[WRITE_FLUSH],
231         profile_p->writecache_flushed_writes[READRAW_FLUSH],
232         profile_p->writecache_flushed_writes[OPLOCK_RELEASE_FLUSH],
233         profile_p->writecache_flushed_writes[CLOSE_FLUSH],
234         profile_p->writecache_flushed_writes[SYNC_FLUSH] ));
235   }
236 #endif
237
238   if(!wcp) {
239     DO_PROFILE_INC(writecache_direct_writes);
240     return real_write_file(fsp, data, pos, n);
241   }
242
243   DEBUG(9,("write_file(fd=%d pos=%.0f size=%u) wcp->offset=%.0f wcp->data_size=%u\n",
244            fsp->fd, (double)pos, (unsigned int)n, (double)wcp->offset, (unsigned int)wcp->data_size));
245
246   /* 
247    * If we have active cache and it isn't contiguous then we flush.
248    * NOTE: There is a small problem with running out of disk ....
249    */
250
251   if (wcp->data_size) {
252
253     BOOL cache_flush_needed = False;
254
255     if ((pos >= wcp->offset) && (pos <= wcp->offset + wcp->data_size)) {
256       
257       /*
258        * Start of write overlaps or abutts the existing data.
259        */
260
261       size_t data_used = MIN((wcp->alloc_size - (pos - wcp->offset)), n);
262
263       memcpy(wcp->data + (pos - wcp->offset), data, data_used);
264
265       /*
266        * Update the current buffer size with the new data.
267        */
268
269       if(pos + data_used > wcp->offset + wcp->data_size)
270         wcp->data_size = pos + data_used - wcp->offset;
271
272       /*
273        * Update the file size if changed.
274        */
275
276       if (wcp->offset + wcp->data_size > wcp->file_size)
277         wcp->file_size = wcp->offset + wcp->data_size;
278
279       /*
280        * If we used all the data then
281        * return here.
282        */
283
284       if(n == data_used)
285         return n;
286       else
287         cache_flush_needed = True;
288
289       /*
290        * Move the start of data forward by the amount used,
291        * cut down the amount left by the same amount.
292        */
293
294       data += data_used;
295       pos += data_used;
296       n -= data_used;
297
298       DO_PROFILE_INC(writecache_abutted_writes);
299       total_written = data_used;
300
301       write_path = 1;
302
303     } else if ((pos < wcp->offset) && (pos + n > wcp->offset) && 
304                (pos + n <= wcp->offset + wcp->alloc_size)) {
305
306       /*
307        * End of write overlaps the existing data.
308        */
309
310       size_t data_used = pos + n - wcp->offset;
311
312       memcpy(wcp->data, data + n - data_used, data_used);
313
314       /*
315        * Update the current buffer size with the new data.
316        */
317
318       if(pos + n > wcp->offset + wcp->data_size)
319         wcp->data_size = pos + n - wcp->offset;
320
321       /*
322        * Update the file size if changed.
323        */
324
325       if (wcp->offset + wcp->data_size > wcp->file_size)
326         wcp->file_size = wcp->offset + wcp->data_size;
327
328       /*
329        * We don't need to move the start of data, but we
330        * cut down the amount left by the amount used.
331        */
332
333       n -= data_used;
334
335       /*
336        * We cannot have used all the data here.
337        */
338
339       cache_flush_needed = True;
340
341       DO_PROFILE_INC(writecache_abutted_writes);
342       total_written = data_used;
343
344       write_path = 2;
345
346     } else if ( (pos >= wcp->file_size) && 
347                 (wcp->offset + wcp->data_size == wcp->file_size) &&
348                 (pos > wcp->offset + wcp->data_size) && 
349                 (pos < wcp->offset + wcp->alloc_size) ) {
350
351       /*
352        * Non-contiguous write part of which fits within
353        * the cache buffer and is extending the file
354        * and the cache contents reflect the current
355        * data up to the current end of the file.
356        */
357
358       size_t data_used;
359
360       if(pos + n <= wcp->offset + wcp->alloc_size)
361         data_used = n;
362       else
363         data_used = wcp->offset + wcp->alloc_size - pos;
364
365       /*
366        * Fill in the non-continuous area with zeros.
367        */
368
369       memset(wcp->data + wcp->data_size, '\0',
370              pos - (wcp->offset + wcp->data_size) );
371
372       memcpy(wcp->data + (pos - wcp->offset), data, data_used);
373
374       /*
375        * Update the current buffer size with the new data.
376        */
377
378       if(pos + data_used > wcp->offset + wcp->data_size)
379         wcp->data_size = pos + data_used - wcp->offset;
380
381       /*
382        * Update the file size if changed.
383        */
384
385       if (wcp->offset + wcp->data_size > wcp->file_size)
386         wcp->file_size = wcp->offset + wcp->data_size;
387
388       /*
389        * If we used all the data then
390        * return here.
391        */
392
393       if(n == data_used)
394         return n;
395       else
396         cache_flush_needed = True;
397
398       /*
399        * Move the start of data forward by the amount used,
400        * cut down the amount left by the same amount.
401        */
402
403       data += data_used;
404       pos += data_used;
405       n -= data_used;
406
407       DO_PROFILE_INC(writecache_abutted_writes);
408       total_written = data_used;
409
410       write_path = 3;
411
412     } else {
413
414       /*
415        * Write is bigger than buffer, or there is no overlap on the
416        * low or high ends.
417        */
418
419       DEBUG(9,("write_file: non cacheable write : fd = %d, pos = %.0f, len = %u, current cache pos = %.0f \
420 len = %u\n",fsp->fd, (double)pos, (unsigned int)n, (double)wcp->offset, (unsigned int)wcp->data_size ));
421
422       /*
423        * Update the file size if needed.
424        */
425
426       if(pos + n > wcp->file_size)
427         wcp->file_size = pos + n;
428
429       /*
430        * If write would fit in the cache, and is larger than
431        * the data already in the cache, flush the cache and
432        * preferentially copy the data new data into it. Otherwise
433        * just write the data directly.
434        */
435
436       if ( n <= wcp->alloc_size && n > wcp->data_size) {
437         cache_flush_needed = True;
438       } else {
439         ssize_t ret = real_write_file(fsp, data, pos, n);
440
441         DO_PROFILE_INC(writecache_direct_writes);
442         if (ret == -1)
443           return ret;
444
445         if (pos + ret > wcp->file_size)
446           wcp->file_size = pos + ret;
447
448         return ret;
449       }
450
451       write_path = 4;
452
453     }
454
455     if(wcp->data_size > wcp->file_size)
456       wcp->file_size = wcp->data_size;
457
458     if (cache_flush_needed) {
459       DEBUG(3,("WRITE_FLUSH:%d: due to noncontinuous write: fd = %d, size = %.0f, pos = %.0f, \
460 n = %u, wcp->offset=%.0f, wcp->data_size=%u\n",
461              write_path, fsp->fd, (double)wcp->file_size, (double)pos, (unsigned int)n,
462              (double)wcp->offset, (unsigned int)wcp->data_size ));
463
464       flush_write_cache(fsp, WRITE_FLUSH);
465     }
466   }
467
468   /*
469    * If the write request is bigger than the cache
470    * size, write it all out.
471    */
472
473   if (n > wcp->alloc_size ) {
474     ssize_t ret = real_write_file(fsp, data, pos, n);
475     if (ret == -1)
476       return -1;
477
478     if (pos + ret > wcp->file_size)
479       wcp->file_size = pos + n;
480
481     DO_PROFILE_INC(writecache_direct_writes);
482     return total_written + n;
483   }
484
485   /*
486    * If there's any data left, cache it.
487    */
488
489   if (n) {
490 #ifdef WITH_PROFILE
491     if (wcp->data_size) {
492       DO_PROFILE_INC(writecache_abutted_writes);
493     } else {
494       DO_PROFILE_INC(writecache_init_writes);
495     }
496 #endif
497     memcpy(wcp->data+wcp->data_size, data, n);
498     if (wcp->data_size == 0) {
499       wcp->offset = pos;
500       DO_PROFILE_INC(writecache_num_write_caches);
501     }
502     wcp->data_size += n;
503
504     /*
505      * Update the file size if changed.
506      */
507
508     if (wcp->offset + wcp->data_size > wcp->file_size)
509       wcp->file_size = wcp->offset + wcp->data_size;
510     DEBUG(9,("wcp->offset = %.0f wcp->data_size = %u cache return %u\n",
511                 (double)wcp->offset, (unsigned int)wcp->data_size, (unsigned int)n));
512
513     total_written += n;
514     return total_written; /* .... that's a write :) */
515   }
516   
517   return total_written;
518 }
519
520 /****************************************************************************
521  Delete the write cache structure.
522 ****************************************************************************/
523
524 void delete_write_cache(files_struct *fsp)
525 {
526   write_cache *wcp;
527
528   if(!fsp)
529     return;
530
531   if(!(wcp = fsp->wcp))
532     return;
533
534   DO_PROFILE_DEC(writecache_allocated_write_caches);
535   allocated_write_caches--;
536
537   SMB_ASSERT(wcp->data_size == 0);
538
539   SAFE_FREE(wcp->data);
540   SAFE_FREE(fsp->wcp);
541
542   DEBUG(10,("delete_write_cache: File %s deleted write cache\n", fsp->fsp_name ));
543
544 }
545
546 /****************************************************************************
547  Setup the write cache structure.
548 ****************************************************************************/
549
550 static BOOL setup_write_cache(files_struct *fsp, SMB_OFF_T file_size)
551 {
552   ssize_t alloc_size = lp_write_cache_size(SNUM(fsp->conn));
553   write_cache *wcp;
554
555   if (allocated_write_caches >= MAX_WRITE_CACHES) 
556         return False;
557
558   if(alloc_size == 0 || fsp->wcp)
559     return False;
560
561   if((wcp = (write_cache *)malloc(sizeof(write_cache))) == NULL) {
562     DEBUG(0,("setup_write_cache: malloc fail.\n"));
563     return False;
564   }
565
566   wcp->file_size = file_size;
567   wcp->offset = 0;
568   wcp->alloc_size = alloc_size;
569   wcp->data_size = 0;
570   if((wcp->data = malloc(wcp->alloc_size)) == NULL) {
571     DEBUG(0,("setup_write_cache: malloc fail for buffer size %u.\n",
572           (unsigned int)wcp->alloc_size ));
573     SAFE_FREE(wcp);
574     return False;
575   }
576
577   memset(wcp->data, '\0', wcp->alloc_size );
578
579   fsp->wcp = wcp;
580   DO_PROFILE_INC(writecache_allocated_write_caches);
581   allocated_write_caches++;
582
583   DEBUG(10,("setup_write_cache: File %s allocated write cache size %u\n",
584                 fsp->fsp_name, wcp->alloc_size ));
585
586   return True;
587 }
588
589 /****************************************************************************
590  Cope with a size change.
591 ****************************************************************************/
592
593 void set_filelen_write_cache(files_struct *fsp, SMB_OFF_T file_size)
594 {
595   if(fsp->wcp) {
596     /* The cache *must* have been flushed before we do this. */
597     if (fsp->wcp->data_size != 0) {
598       pstring msg;
599       slprintf(msg, sizeof(msg)-1, "set_filelen_write_cache: size change \
600 on file %s with write cache size = %u\n", fsp->fsp_name, fsp->wcp->data_size );
601       smb_panic(msg);
602     }
603     fsp->wcp->file_size = file_size;
604   }
605 }
606
607 /*******************************************************************
608  Flush a write cache struct to disk.
609 ********************************************************************/
610
611 ssize_t flush_write_cache(files_struct *fsp, enum flush_reason_enum reason)
612 {
613   write_cache *wcp = fsp->wcp;
614   size_t data_size;
615   ssize_t ret;
616
617   if(!wcp || !wcp->data_size)
618     return 0;
619
620   data_size = wcp->data_size;
621   wcp->data_size = 0;
622
623   DO_PROFILE_DEC_INC(writecache_num_write_caches,writecache_flushed_writes[reason]);
624
625   DEBUG(9,("flushing write cache: fd = %d, off=%.0f, size=%u\n",
626            fsp->fd, (double)wcp->offset, (unsigned int)data_size));
627
628 #ifdef WITH_PROFILE
629   if(data_size == wcp->alloc_size)
630     DO_PROFILE_INC(writecache_num_perfect_writes);
631 #endif
632
633   ret = real_write_file(fsp, wcp->data, wcp->offset, data_size);
634
635   /*
636    * Ensure file size if kept up to date if write extends file.
637    */
638
639   if ((ret != -1) && (wcp->offset + ret > wcp->file_size))
640     wcp->file_size = wcp->offset + ret;
641
642   return ret;
643 }
644
645 /*******************************************************************
646 sync a file
647 ********************************************************************/
648
649 void sync_file(connection_struct *conn, files_struct *fsp)
650 {
651     if(lp_strict_sync(SNUM(conn)) && fsp->fd != -1) {
652       flush_write_cache(fsp, SYNC_FLUSH);
653       conn->vfs_ops.fsync(fsp,fsp->fd);
654     }
655 }