r24164: Fix for write cache corruption bug reported by Jean-Francois Panisset <paniss...
[tprouty/samba.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    Copyright (C) Jeremy Allison 2000-2002. - write cache.
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23
24 static BOOL setup_write_cache(files_struct *, SMB_OFF_T);
25
26 /****************************************************************************
27  Read from write cache if we can.
28 ****************************************************************************/
29
30 static BOOL read_from_write_cache(files_struct *fsp,char *data,SMB_OFF_T pos,size_t n)
31 {
32         write_cache *wcp = fsp->wcp;
33
34         if(!wcp) {
35                 return False;
36         }
37
38         if( n > wcp->data_size || pos < wcp->offset || pos + n > wcp->offset + wcp->data_size) {
39                 return False;
40         }
41
42         memcpy(data, wcp->data + (pos - wcp->offset), n);
43
44         DO_PROFILE_INC(writecache_read_hits);
45
46         return True;
47 }
48
49 /****************************************************************************
50  Read from a file.
51 ****************************************************************************/
52
53 ssize_t read_file(files_struct *fsp,char *data,SMB_OFF_T pos,size_t n)
54 {
55         ssize_t ret=0,readret;
56
57         /* you can't read from print files */
58         if (fsp->print_file) {
59                 return -1;
60         }
61
62         /*
63          * Serve from write cache if we can.
64          */
65
66         if(read_from_write_cache(fsp, data, pos, n)) {
67                 fsp->fh->pos = pos + n;
68                 fsp->fh->position_information = fsp->fh->pos;
69                 return n;
70         }
71
72         flush_write_cache(fsp, READ_FLUSH);
73
74         fsp->fh->pos = pos;
75
76         if (n > 0) {
77 #ifdef DMF_FIX
78                 int numretries = 3;
79 tryagain:
80                 readret = SMB_VFS_PREAD(fsp,fsp->fh->fd,data,n,pos);
81
82                 if (readret == -1) {
83                         if ((errno == EAGAIN) && numretries) {
84                                 DEBUG(3,("read_file EAGAIN retry in 10 seconds\n"));
85                                 (void)sleep(10);
86                                 --numretries;
87                                 goto tryagain;
88                         }
89                         return -1;
90                 }
91 #else /* NO DMF fix. */
92                 readret = SMB_VFS_PREAD(fsp,fsp->fh->fd,data,n,pos);
93
94                 if (readret == -1) {
95                         return -1;
96                 }
97 #endif
98                 if (readret > 0) {
99                         ret += readret;
100                 }
101         }
102
103         DEBUG(10,("read_file (%s): pos = %.0f, size = %lu, returned %lu\n",
104                 fsp->fsp_name, (double)pos, (unsigned long)n, (long)ret ));
105
106         fsp->fh->pos += ret;
107         fsp->fh->position_information = fsp->fh->pos;
108
109         return(ret);
110 }
111
112 /* how many write cache buffers have been allocated */
113 static unsigned int allocated_write_caches;
114
115 /****************************************************************************
116  *Really* write to a file.
117 ****************************************************************************/
118
119 static ssize_t real_write_file(files_struct *fsp,const char *data, SMB_OFF_T pos, size_t n)
120 {
121         ssize_t ret;
122
123         if (pos == -1) {
124                 ret = vfs_write_data(fsp, data, n);
125         } else {
126                 fsp->fh->pos = pos;
127                 if (pos && lp_strict_allocate(SNUM(fsp->conn))) {
128                         if (vfs_fill_sparse(fsp, pos) == -1) {
129                                 return -1;
130                         }
131                 }
132                 ret = vfs_pwrite_data(fsp, data, n, pos);
133         }
134
135         DEBUG(10,("real_write_file (%s): pos = %.0f, size = %lu, returned %ld\n",
136                 fsp->fsp_name, (double)pos, (unsigned long)n, (long)ret ));
137
138         if (ret != -1) {
139                 fsp->fh->pos += ret;
140
141                 /*
142                  * It turns out that setting the last write time from a Windows
143                  * client stops any subsequent writes from updating the write time.
144                  * Doing this after the write gives a race condition here where
145                  * a stat may see the changed write time before we reset it here,
146                  * but it's cheaper than having to store the write time in shared
147                  * memory and look it up using dev/inode across all running smbd's.
148                  * The 99% solution will hopefully be good enough in this case. JRA.
149                  */
150
151                 if (!null_timespec(fsp->pending_modtime)) {
152                         set_filetime(fsp->conn, fsp->fsp_name, fsp->pending_modtime);
153
154                         /* If we didn't get the "set modtime" call ourselves, we must
155                            store the last write time to restore on close. JRA. */
156                         if (!fsp->pending_modtime_owner) {
157                                 fsp->last_write_time = timespec_current();
158                         }
159                 }
160
161 /* Yes - this is correct - writes don't update this. JRA. */
162 /* Found by Samba4 tests. */
163 #if 0
164                 fsp->position_information = fsp->pos;
165 #endif
166         }
167
168         return ret;
169 }
170
171 /****************************************************************************
172  File size cache change.
173  Updates size on disk but doesn't flush the cache.
174 ****************************************************************************/
175
176 static int wcp_file_size_change(files_struct *fsp)
177 {
178         int ret;
179         write_cache *wcp = fsp->wcp;
180
181         wcp->file_size = wcp->offset + wcp->data_size;
182         ret = SMB_VFS_FTRUNCATE(fsp, fsp->fh->fd, wcp->file_size);
183         if (ret == -1) {
184                 DEBUG(0,("wcp_file_size_change (%s): ftruncate of size %.0f error %s\n",
185                         fsp->fsp_name, (double)wcp->file_size, strerror(errno) ));
186         }
187         return ret;
188 }
189
190 /****************************************************************************
191  Write to a file.
192 ****************************************************************************/
193
194 ssize_t write_file(files_struct *fsp, const char *data, SMB_OFF_T pos, size_t n)
195 {
196         write_cache *wcp = fsp->wcp;
197         ssize_t total_written = 0;
198         int write_path = -1; 
199
200         if (fsp->print_file) {
201                 fstring sharename;
202                 uint32 jobid;
203
204                 if (!rap_to_pjobid(fsp->rap_print_jobid, sharename, &jobid)) {
205                         DEBUG(3,("write_file: Unable to map RAP jobid %u to jobid.\n",
206                                                 (unsigned int)fsp->rap_print_jobid ));
207                         errno = EBADF;
208                         return -1;
209                 }
210
211                 return print_job_write(SNUM(fsp->conn), jobid, data, pos, n);
212         }
213
214         if (!fsp->can_write) {
215                 errno = EPERM;
216                 return -1;
217         }
218
219         if (!fsp->modified) {
220                 SMB_STRUCT_STAT st;
221                 fsp->modified = True;
222
223                 if (SMB_VFS_FSTAT(fsp,fsp->fh->fd,&st) == 0) {
224                         int dosmode = dos_mode(fsp->conn,fsp->fsp_name,&st);
225                         if ((lp_store_dos_attributes(SNUM(fsp->conn)) || MAP_ARCHIVE(fsp->conn)) && !IS_DOS_ARCHIVE(dosmode)) {
226                                 file_set_dosmode(fsp->conn,fsp->fsp_name,dosmode | aARCH,&st, False);
227                         }
228
229                         /*
230                          * If this is the first write and we have an exclusive oplock then setup
231                          * the write cache.
232                          */
233
234                         if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type) && !wcp) {
235                                 setup_write_cache(fsp, st.st_size);
236                                 wcp = fsp->wcp;
237                         } 
238                 }  
239         }
240
241 #ifdef WITH_PROFILE
242         DO_PROFILE_INC(writecache_total_writes);
243         if (!fsp->oplock_type) {
244                 DO_PROFILE_INC(writecache_non_oplock_writes);
245         }
246 #endif
247
248         /*
249          * If this file is level II oplocked then we need
250          * to grab the shared memory lock and inform all
251          * other files with a level II lock that they need
252          * to flush their read caches. We keep the lock over
253          * the shared memory area whilst doing this.
254          */
255
256         release_level_2_oplocks_on_change(fsp);
257
258 #ifdef WITH_PROFILE
259         if (profile_p && profile_p->writecache_total_writes % 500 == 0) {
260                 DEBUG(3,("WRITECACHE: initwrites=%u abutted=%u total=%u \
261 nonop=%u allocated=%u active=%u direct=%u perfect=%u readhits=%u\n",
262                         profile_p->writecache_init_writes,
263                         profile_p->writecache_abutted_writes,
264                         profile_p->writecache_total_writes,
265                         profile_p->writecache_non_oplock_writes,
266                         profile_p->writecache_allocated_write_caches,
267                         profile_p->writecache_num_write_caches,
268                         profile_p->writecache_direct_writes,
269                         profile_p->writecache_num_perfect_writes,
270                         profile_p->writecache_read_hits ));
271
272                 DEBUG(3,("WRITECACHE: Flushes SEEK=%d, READ=%d, WRITE=%d, READRAW=%d, OPLOCK=%d, CLOSE=%d, SYNC=%d\n",
273                         profile_p->writecache_flushed_writes[SEEK_FLUSH],
274                         profile_p->writecache_flushed_writes[READ_FLUSH],
275                         profile_p->writecache_flushed_writes[WRITE_FLUSH],
276                         profile_p->writecache_flushed_writes[READRAW_FLUSH],
277                         profile_p->writecache_flushed_writes[OPLOCK_RELEASE_FLUSH],
278                         profile_p->writecache_flushed_writes[CLOSE_FLUSH],
279                         profile_p->writecache_flushed_writes[SYNC_FLUSH] ));
280         }
281 #endif
282
283         if(!wcp) {
284                 DO_PROFILE_INC(writecache_direct_writes);
285                 total_written = real_write_file(fsp, data, pos, n);
286                 return total_written;
287         }
288
289         DEBUG(9,("write_file (%s)(fd=%d pos=%.0f size=%u) wcp->offset=%.0f wcp->data_size=%u\n",
290                 fsp->fsp_name, fsp->fh->fd, (double)pos, (unsigned int)n, (double)wcp->offset, (unsigned int)wcp->data_size));
291
292         fsp->fh->pos = pos + n;
293
294         /* 
295          * If we have active cache and it isn't contiguous then we flush.
296          * NOTE: There is a small problem with running out of disk ....
297          */
298
299         if (wcp->data_size) {
300                 BOOL cache_flush_needed = False;
301
302                 if ((pos >= wcp->offset) && (pos <= wcp->offset + wcp->data_size)) {
303       
304                         /* ASCII art.... JRA.
305
306       +--------------+-----
307       | Cached data  | Rest of allocated cache buffer....
308       +--------------+-----
309
310             +-------------------+
311             | Data to write     |
312             +-------------------+
313
314                         */
315
316                         /*
317                          * Start of write overlaps or abutts the existing data.
318                          */
319
320                         size_t data_used = MIN((wcp->alloc_size - (pos - wcp->offset)), n);
321
322                         memcpy(wcp->data + (pos - wcp->offset), data, data_used);
323
324                         /*
325                          * Update the current buffer size with the new data.
326                          */
327
328                         if(pos + data_used > wcp->offset + wcp->data_size) {
329                                 wcp->data_size = pos + data_used - wcp->offset;
330                         }
331
332                         /*
333                          * Update the file size if changed.
334                          */
335
336                         if (wcp->offset + wcp->data_size > wcp->file_size) {
337                                 if (wcp_file_size_change(fsp) == -1) {
338                                         return -1;
339                                 }
340                         }
341
342                         /*
343                          * If we used all the data then
344                          * return here.
345                          */
346
347                         if(n == data_used) {
348                                 return n;
349                         } else {
350                                 cache_flush_needed = True;
351                         }
352                         /*
353                          * Move the start of data forward by the amount used,
354                          * cut down the amount left by the same amount.
355                          */
356
357                         data += data_used;
358                         pos += data_used;
359                         n -= data_used;
360
361                         DO_PROFILE_INC(writecache_abutted_writes);
362                         total_written = data_used;
363
364                         write_path = 1;
365
366                 } else if ((pos < wcp->offset) && (pos + n > wcp->offset) && 
367                                         (pos + n <= wcp->offset + wcp->alloc_size)) {
368
369                         /* ASCII art.... JRA.
370
371                         +---------------+
372                         | Cache buffer  |
373                         +---------------+
374
375             +-------------------+
376             | Data to write     |
377             +-------------------+
378
379                         */
380
381                         /*
382                          * End of write overlaps the existing data.
383                          */
384
385                         size_t data_used = pos + n - wcp->offset;
386
387                         memcpy(wcp->data, data + n - data_used, data_used);
388
389                         /*
390                          * Update the current buffer size with the new data.
391                          */
392
393                         if(pos + n > wcp->offset + wcp->data_size) {
394                                 wcp->data_size = pos + n - wcp->offset;
395                         }
396
397                         /*
398                          * Update the file size if changed.
399                          */
400
401                         if (wcp->offset + wcp->data_size > wcp->file_size) {
402                                 if (wcp_file_size_change(fsp) == -1) {
403                                         return -1;
404                                 }
405                         }
406
407                         /*
408                          * We don't need to move the start of data, but we
409                          * cut down the amount left by the amount used.
410                          */
411
412                         n -= data_used;
413
414                         /*
415                          * We cannot have used all the data here.
416                          */
417
418                         cache_flush_needed = True;
419
420                         DO_PROFILE_INC(writecache_abutted_writes);
421                         total_written = data_used;
422
423                         write_path = 2;
424
425                 } else if ( (pos >= wcp->file_size) && 
426                                         (wcp->offset + wcp->data_size == wcp->file_size) &&
427                                         (pos > wcp->offset + wcp->data_size) && 
428                                         (pos < wcp->offset + wcp->alloc_size) ) {
429
430                         /* ASCII art.... JRA.
431
432                        End of file ---->|
433
434                         +---------------+---------------+
435                         | Cached data   | Cache buffer  |
436                         +---------------+---------------+
437
438                                               +-------------------+
439                                               | Data to write     |
440                                               +-------------------+
441
442                         */
443
444                         /*
445                          * Non-contiguous write part of which fits within
446                          * the cache buffer and is extending the file
447                          * and the cache contents reflect the current
448                          * data up to the current end of the file.
449                          */
450
451                         size_t data_used;
452
453                         if(pos + n <= wcp->offset + wcp->alloc_size) {
454                                 data_used = n;
455                         } else {
456                                 data_used = wcp->offset + wcp->alloc_size - pos;
457                         }
458
459                         /*
460                          * Fill in the non-continuous area with zeros.
461                          */
462
463                         memset(wcp->data + wcp->data_size, '\0',
464                                 pos - (wcp->offset + wcp->data_size) );
465
466                         memcpy(wcp->data + (pos - wcp->offset), data, data_used);
467
468                         /*
469                          * Update the current buffer size with the new data.
470                          */
471
472                         if(pos + data_used > wcp->offset + wcp->data_size) {
473                                 wcp->data_size = pos + data_used - wcp->offset;
474                         }
475
476                         /*
477                          * Update the file size if changed.
478                          */
479
480                         if (wcp->offset + wcp->data_size > wcp->file_size) {
481                                 if (wcp_file_size_change(fsp) == -1) {
482                                         return -1;
483                                 }
484                         }
485
486                         /*
487                          * If we used all the data then
488                          * return here.
489                          */
490
491                         if(n == data_used) {
492                                 return n;
493                         } else {
494                                 cache_flush_needed = True;
495                         }
496
497                         /*
498                          * Move the start of data forward by the amount used,
499                          * cut down the amount left by the same amount.
500                          */
501
502                         data += data_used;
503                         pos += data_used;
504                         n -= data_used;
505
506                         DO_PROFILE_INC(writecache_abutted_writes);
507                         total_written = data_used;
508
509                         write_path = 3;
510
511                 } else if ( (pos >= wcp->file_size) &&
512                             (n == 1) &&
513                             (wcp->file_size == wcp->offset + wcp->data_size) &&
514                             (pos < wcp->file_size + wcp->alloc_size)) {
515
516                         /*
517
518                 End of file ---->|
519
520                  +---------------+---------------+
521                  | Cached data   | Cache buffer  |
522                  +---------------+---------------+
523
524                                  |<------- allocated size ---------------->|
525
526                                                          +--------+
527                                                          | 1 Byte |
528                                                          +--------+
529
530                         MS-Office seems to do this a lot to determine if there's enough
531                         space on the filesystem to write a new file.
532
533                         Change to :
534
535                 End of file ---->|
536                                  +-----------------------+--------+
537                                  | Zeroed Cached data    | 1 Byte |
538                                  +-----------------------+--------+
539                         */
540
541                         flush_write_cache(fsp, WRITE_FLUSH);
542                         wcp->offset = wcp->file_size;
543                         wcp->data_size = pos - wcp->file_size + 1;
544                         memset(wcp->data, '\0', wcp->data_size);
545                         memcpy(wcp->data + wcp->data_size-1, data, 1);
546
547                         /*
548                          * Update the file size if changed.
549                          */
550
551                         if (wcp->offset + wcp->data_size > wcp->file_size) {
552                                 if (wcp_file_size_change(fsp) == -1) {
553                                         return -1;
554                                 }
555                         }
556
557                         return n;
558
559                 } else {
560
561                         /* ASCII art..... JRA.
562
563    Case 1).
564
565                         +---------------+---------------+
566                         | Cached data   | Cache buffer  |
567                         +---------------+---------------+
568
569                                                               +-------------------+
570                                                               | Data to write     |
571                                                               +-------------------+
572
573    Case 2).
574
575                            +---------------+---------------+
576                            | Cached data   | Cache buffer  |
577                            +---------------+---------------+
578
579    +-------------------+
580    | Data to write     |
581    +-------------------+
582
583     Case 3).
584
585                            +---------------+---------------+
586                            | Cached data   | Cache buffer  |
587                            +---------------+---------------+
588
589                   +-----------------------------------------------------+
590                   | Data to write                                       |
591                   +-----------------------------------------------------+
592
593                   */
594
595                         /*
596                          * Write is bigger than buffer, or there is no overlap on the
597                          * low or high ends.
598                          */
599
600                         DEBUG(9,("write_file: non cacheable write : fd = %d, pos = %.0f, len = %u, current cache pos = %.0f \
601 len = %u\n",fsp->fh->fd, (double)pos, (unsigned int)n, (double)wcp->offset, (unsigned int)wcp->data_size ));
602
603                         /*
604                          * If write would fit in the cache, and is larger than
605                          * the data already in the cache, flush the cache and
606                          * preferentially copy the data new data into it. Otherwise
607                          * just write the data directly.
608                          */
609
610                         if ( n <= wcp->alloc_size && n > wcp->data_size) {
611                                 cache_flush_needed = True;
612                         } else {
613                                 ssize_t ret = real_write_file(fsp, data, pos, n);
614
615                                 /*
616                                  * If the write overlaps the entire cache, then
617                                  * discard the current contents of the cache.
618                                  * Fix from Rasmus Borup Hansen rbh@math.ku.dk.
619                                  */
620
621                                 if ((pos <= wcp->offset) &&
622                                                 (pos + n >= wcp->offset + wcp->data_size) ) {
623                                         DEBUG(9,("write_file: discarding overwritten write \
624 cache: fd = %d, off=%.0f, size=%u\n", fsp->fh->fd, (double)wcp->offset, (unsigned int)wcp->data_size ));
625                                         wcp->data_size = 0;
626                                 }
627
628                                 DO_PROFILE_INC(writecache_direct_writes);
629                                 if (ret == -1) {
630                                         return ret;
631                                 }
632
633                                 if (pos + ret > wcp->file_size) {
634                                         wcp->file_size = pos + ret;
635                                 }
636
637                                 return ret;
638                         }
639
640                         write_path = 4;
641
642                 }
643
644                 if (cache_flush_needed) {
645                         DEBUG(3,("WRITE_FLUSH:%d: due to noncontinuous write: fd = %d, size = %.0f, pos = %.0f, \
646 n = %u, wcp->offset=%.0f, wcp->data_size=%u\n",
647                                 write_path, fsp->fh->fd, (double)wcp->file_size, (double)pos, (unsigned int)n,
648                                 (double)wcp->offset, (unsigned int)wcp->data_size ));
649
650                         flush_write_cache(fsp, WRITE_FLUSH);
651                 }
652         }
653
654         /*
655          * If the write request is bigger than the cache
656          * size, write it all out.
657          */
658
659         if (n > wcp->alloc_size ) {
660                 ssize_t ret = real_write_file(fsp, data, pos, n);
661                 if (ret == -1) {
662                         return -1;
663                 }
664
665                 if (pos + ret > wcp->file_size) {
666                         wcp->file_size = pos + n;
667                 }
668
669                 DO_PROFILE_INC(writecache_direct_writes);
670                 return total_written + n;
671         }
672
673         /*
674          * If there's any data left, cache it.
675          */
676
677         if (n) {
678 #ifdef WITH_PROFILE
679                 if (wcp->data_size) {
680                         DO_PROFILE_INC(writecache_abutted_writes);
681                 } else {
682                         DO_PROFILE_INC(writecache_init_writes);
683                 }
684 #endif
685                 memcpy(wcp->data+wcp->data_size, data, n);
686                 if (wcp->data_size == 0) {
687                         wcp->offset = pos;
688                         DO_PROFILE_INC(writecache_num_write_caches);
689                 }
690                 wcp->data_size += n;
691
692                 /*
693                  * Update the file size if changed.
694                  */
695
696                 if (wcp->offset + wcp->data_size > wcp->file_size) {
697                         if (wcp_file_size_change(fsp) == -1) {
698                                 return -1;
699                         }
700                 }
701                 DEBUG(9,("wcp->offset = %.0f wcp->data_size = %u cache return %u\n",
702                         (double)wcp->offset, (unsigned int)wcp->data_size, (unsigned int)n));
703
704                 total_written += n;
705                 return total_written; /* .... that's a write :) */
706         }
707   
708         return total_written;
709 }
710
711 /****************************************************************************
712  Delete the write cache structure.
713 ****************************************************************************/
714
715 void delete_write_cache(files_struct *fsp)
716 {
717         write_cache *wcp;
718
719         if(!fsp) {
720                 return;
721         }
722
723         if(!(wcp = fsp->wcp)) {
724                 return;
725         }
726
727         DO_PROFILE_DEC(writecache_allocated_write_caches);
728         allocated_write_caches--;
729
730         SMB_ASSERT(wcp->data_size == 0);
731
732         SAFE_FREE(wcp->data);
733         SAFE_FREE(fsp->wcp);
734
735         DEBUG(10,("delete_write_cache: File %s deleted write cache\n", fsp->fsp_name ));
736 }
737
738 /****************************************************************************
739  Setup the write cache structure.
740 ****************************************************************************/
741
742 static BOOL setup_write_cache(files_struct *fsp, SMB_OFF_T file_size)
743 {
744         ssize_t alloc_size = lp_write_cache_size(SNUM(fsp->conn));
745         write_cache *wcp;
746
747         if (allocated_write_caches >= MAX_WRITE_CACHES) {
748                 return False;
749         }
750
751         if(alloc_size == 0 || fsp->wcp) {
752                 return False;
753         }
754
755         if((wcp = SMB_MALLOC_P(write_cache)) == NULL) {
756                 DEBUG(0,("setup_write_cache: malloc fail.\n"));
757                 return False;
758         }
759
760         wcp->file_size = file_size;
761         wcp->offset = 0;
762         wcp->alloc_size = alloc_size;
763         wcp->data_size = 0;
764         if((wcp->data = (char *)SMB_MALLOC(wcp->alloc_size)) == NULL) {
765                 DEBUG(0,("setup_write_cache: malloc fail for buffer size %u.\n",
766                         (unsigned int)wcp->alloc_size ));
767                 SAFE_FREE(wcp);
768                 return False;
769         }
770
771         memset(wcp->data, '\0', wcp->alloc_size );
772
773         fsp->wcp = wcp;
774         DO_PROFILE_INC(writecache_allocated_write_caches);
775         allocated_write_caches++;
776
777         DEBUG(10,("setup_write_cache: File %s allocated write cache size %lu\n",
778                 fsp->fsp_name, (unsigned long)wcp->alloc_size ));
779
780         return True;
781 }
782
783 /****************************************************************************
784  Cope with a size change.
785 ****************************************************************************/
786
787 void set_filelen_write_cache(files_struct *fsp, SMB_OFF_T file_size)
788 {
789         if(fsp->wcp) {
790                 /* The cache *must* have been flushed before we do this. */
791                 if (fsp->wcp->data_size != 0) {
792                         char *msg;
793                         asprintf(&msg, "set_filelen_write_cache: size change "
794                                  "on file %s with write cache size = %lu\n",
795                                  fsp->fsp_name,
796                                  (unsigned long)fsp->wcp->data_size);
797                         smb_panic(msg);
798                 }
799                 fsp->wcp->file_size = file_size;
800         }
801 }
802
803 /*******************************************************************
804  Flush a write cache struct to disk.
805 ********************************************************************/
806
807 ssize_t flush_write_cache(files_struct *fsp, enum flush_reason_enum reason)
808 {
809         write_cache *wcp = fsp->wcp;
810         size_t data_size;
811         ssize_t ret;
812
813         if(!wcp || !wcp->data_size) {
814                 return 0;
815         }
816
817         data_size = wcp->data_size;
818         wcp->data_size = 0;
819
820         DO_PROFILE_DEC_INC(writecache_num_write_caches,writecache_flushed_writes[reason]);
821
822         DEBUG(9,("flushing write cache: fd = %d, off=%.0f, size=%u\n",
823                 fsp->fh->fd, (double)wcp->offset, (unsigned int)data_size));
824
825 #ifdef WITH_PROFILE
826         if(data_size == wcp->alloc_size) {
827                 DO_PROFILE_INC(writecache_num_perfect_writes);
828         }
829 #endif
830
831         ret = real_write_file(fsp, wcp->data, wcp->offset, data_size);
832
833         /*
834          * Ensure file size if kept up to date if write extends file.
835          */
836
837         if ((ret != -1) && (wcp->offset + ret > wcp->file_size)) {
838                 wcp->file_size = wcp->offset + ret;
839         }
840
841         return ret;
842 }
843
844 /*******************************************************************
845 sync a file
846 ********************************************************************/
847
848 NTSTATUS sync_file(connection_struct *conn, files_struct *fsp, BOOL write_through)
849 {
850         if (fsp->fh->fd == -1)
851                 return NT_STATUS_INVALID_HANDLE;
852
853         if (lp_strict_sync(SNUM(conn)) &&
854             (lp_syncalways(SNUM(conn)) || write_through)) {
855                 int ret = flush_write_cache(fsp, SYNC_FLUSH);
856                 if (ret == -1) {
857                         return map_nt_error_from_unix(errno);
858                 }
859                 ret = SMB_VFS_FSYNC(fsp,fsp->fh->fd);
860                 if (ret == -1) {
861                         return map_nt_error_from_unix(errno);
862                 }
863         }
864         return NT_STATUS_OK;
865 }
866
867 /************************************************************
868  Perform a stat whether a valid fd or not.
869 ************************************************************/
870
871 int fsp_stat(files_struct *fsp, SMB_STRUCT_STAT *pst)
872 {
873         if (fsp->fh->fd == -1) {
874                 return SMB_VFS_STAT(fsp->conn, fsp->fsp_name, pst);
875         } else {
876                 return SMB_VFS_FSTAT(fsp,fsp->fh->fd, pst);
877         }
878 }