smbd: implement the strange write time update logic
[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,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,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(struct smb_request *req,
120                                 files_struct *fsp,
121                                 const char *data,
122                                 SMB_OFF_T pos,
123                                 size_t n)
124 {
125         ssize_t ret;
126
127         if (pos == -1) {
128                 ret = vfs_write_data(req, fsp, data, n);
129         } else {
130                 fsp->fh->pos = pos;
131                 if (pos && lp_strict_allocate(SNUM(fsp->conn))) {
132                         if (vfs_fill_sparse(fsp, pos) == -1) {
133                                 return -1;
134                         }
135                 }
136                 ret = vfs_pwrite_data(req, fsp, data, n, pos);
137         }
138
139         DEBUG(10,("real_write_file (%s): pos = %.0f, size = %lu, returned %ld\n",
140                 fsp->fsp_name, (double)pos, (unsigned long)n, (long)ret ));
141
142         if (ret != -1) {
143                 fsp->fh->pos += ret;
144
145 /* Yes - this is correct - writes don't update this. JRA. */
146 /* Found by Samba4 tests. */
147 #if 0
148                 fsp->position_information = fsp->pos;
149 #endif
150         }
151
152         return ret;
153 }
154
155 /****************************************************************************
156  File size cache change.
157  Updates size on disk but doesn't flush the cache.
158 ****************************************************************************/
159
160 static int wcp_file_size_change(files_struct *fsp)
161 {
162         int ret;
163         write_cache *wcp = fsp->wcp;
164
165         wcp->file_size = wcp->offset + wcp->data_size;
166         ret = SMB_VFS_FTRUNCATE(fsp, wcp->file_size);
167         if (ret == -1) {
168                 DEBUG(0,("wcp_file_size_change (%s): ftruncate of size %.0f error %s\n",
169                         fsp->fsp_name, (double)wcp->file_size, strerror(errno) ));
170         }
171         return ret;
172 }
173
174 static void update_write_time_handler(struct event_context *ctx,
175                                       struct timed_event *te,
176                                       const struct timeval *now,
177                                       void *private_data)
178 {
179        files_struct *fsp = (files_struct *)private_data;
180
181        /* Remove the timed event handler. */
182        TALLOC_FREE(fsp->update_write_time_event);
183        DEBUG(5, ("Update write time on %s\n", fsp->fsp_name));
184
185        /* change the write time if not already changed by someoneelse */
186        set_write_time_fsp(fsp, timespec_current(), false);
187 }
188
189 void trigger_write_time_update(struct files_struct *fsp)
190 {
191         if (fsp->write_time_forced) {
192                 return;
193         }
194
195         if (fsp->update_write_time_triggered) {
196                 return;
197         }
198         fsp->update_write_time_triggered = true;
199
200         /* trigger the update 2 seconds later */
201         fsp->update_write_time_on_close = true;
202         fsp->update_write_time_event =
203                 event_add_timed(smbd_event_context(), NULL,
204                                 timeval_current_ofs(2, 0),
205                                 "update_write_time_handler",
206                                 update_write_time_handler, fsp);
207 }
208
209 /****************************************************************************
210  Write to a file.
211 ****************************************************************************/
212
213 ssize_t write_file(struct smb_request *req,
214                         files_struct *fsp,
215                         const char *data,
216                         SMB_OFF_T pos,
217                         size_t n)
218 {
219         write_cache *wcp = fsp->wcp;
220         ssize_t total_written = 0;
221         int write_path = -1;
222
223         if (fsp->print_file) {
224                 fstring sharename;
225                 uint32 jobid;
226
227                 if (!rap_to_pjobid(fsp->rap_print_jobid, sharename, &jobid)) {
228                         DEBUG(3,("write_file: Unable to map RAP jobid %u to jobid.\n",
229                                                 (unsigned int)fsp->rap_print_jobid ));
230                         errno = EBADF;
231                         return -1;
232                 }
233
234                 return print_job_write(SNUM(fsp->conn), jobid, data, pos, n);
235         }
236
237         if (!fsp->can_write) {
238                 errno = EPERM;
239                 return -1;
240         }
241
242         if (!fsp->modified) {
243                 SMB_STRUCT_STAT st;
244                 fsp->modified = True;
245
246                 if (SMB_VFS_FSTAT(fsp, &st) == 0) {
247                         int dosmode;
248                         trigger_write_time_update(fsp);
249                         dosmode = dos_mode(fsp->conn,fsp->fsp_name,&st);
250                         if ((lp_store_dos_attributes(SNUM(fsp->conn)) ||
251                                         MAP_ARCHIVE(fsp->conn)) &&
252                                         !IS_DOS_ARCHIVE(dosmode)) {
253                                 file_set_dosmode(fsp->conn,fsp->fsp_name,
254                                                 dosmode | aARCH,&st,
255                                                 NULL,
256                                                 false);
257                         }
258
259                         /*
260                          * If this is the first write and we have an exclusive oplock then setup
261                          * the write cache.
262                          */
263
264                         if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type) && !wcp) {
265                                 setup_write_cache(fsp, st.st_size);
266                                 wcp = fsp->wcp;
267                         }
268                 }
269         }
270
271 #ifdef WITH_PROFILE
272         DO_PROFILE_INC(writecache_total_writes);
273         if (!fsp->oplock_type) {
274                 DO_PROFILE_INC(writecache_non_oplock_writes);
275         }
276 #endif
277
278         /*
279          * If this file is level II oplocked then we need
280          * to grab the shared memory lock and inform all
281          * other files with a level II lock that they need
282          * to flush their read caches. We keep the lock over
283          * the shared memory area whilst doing this.
284          */
285
286         release_level_2_oplocks_on_change(fsp);
287
288 #ifdef WITH_PROFILE
289         if (profile_p && profile_p->writecache_total_writes % 500 == 0) {
290                 DEBUG(3,("WRITECACHE: initwrites=%u abutted=%u total=%u \
291 nonop=%u allocated=%u active=%u direct=%u perfect=%u readhits=%u\n",
292                         profile_p->writecache_init_writes,
293                         profile_p->writecache_abutted_writes,
294                         profile_p->writecache_total_writes,
295                         profile_p->writecache_non_oplock_writes,
296                         profile_p->writecache_allocated_write_caches,
297                         profile_p->writecache_num_write_caches,
298                         profile_p->writecache_direct_writes,
299                         profile_p->writecache_num_perfect_writes,
300                         profile_p->writecache_read_hits ));
301
302                 DEBUG(3,("WRITECACHE: Flushes SEEK=%d, READ=%d, WRITE=%d, READRAW=%d, OPLOCK=%d, CLOSE=%d, SYNC=%d\n",
303                         profile_p->writecache_flushed_writes[SEEK_FLUSH],
304                         profile_p->writecache_flushed_writes[READ_FLUSH],
305                         profile_p->writecache_flushed_writes[WRITE_FLUSH],
306                         profile_p->writecache_flushed_writes[READRAW_FLUSH],
307                         profile_p->writecache_flushed_writes[OPLOCK_RELEASE_FLUSH],
308                         profile_p->writecache_flushed_writes[CLOSE_FLUSH],
309                         profile_p->writecache_flushed_writes[SYNC_FLUSH] ));
310         }
311 #endif
312
313         if (wcp && req->unread_bytes) {
314                 /* If we're using receivefile don't
315                  * deal with a write cache.
316                  */
317                 flush_write_cache(fsp, WRITE_FLUSH);
318                 delete_write_cache(fsp);
319                 wcp = NULL;
320         }
321
322         if(!wcp) {
323                 DO_PROFILE_INC(writecache_direct_writes);
324                 total_written = real_write_file(req, fsp, data, pos, n);
325                 return total_written;
326         }
327
328         DEBUG(9,("write_file (%s)(fd=%d pos=%.0f size=%u) wcp->offset=%.0f wcp->data_size=%u\n",
329                 fsp->fsp_name, fsp->fh->fd, (double)pos, (unsigned int)n, (double)wcp->offset, (unsigned int)wcp->data_size));
330
331         fsp->fh->pos = pos + n;
332
333         /*
334          * If we have active cache and it isn't contiguous then we flush.
335          * NOTE: There is a small problem with running out of disk ....
336          */
337
338         if (wcp->data_size) {
339                 bool cache_flush_needed = False;
340
341                 if ((pos >= wcp->offset) && (pos <= wcp->offset + wcp->data_size)) {
342       
343                         /* ASCII art.... JRA.
344
345       +--------------+-----
346       | Cached data  | Rest of allocated cache buffer....
347       +--------------+-----
348
349             +-------------------+
350             | Data to write     |
351             +-------------------+
352
353                         */
354
355                         /*
356                          * Start of write overlaps or abutts the existing data.
357                          */
358
359                         size_t data_used = MIN((wcp->alloc_size - (pos - wcp->offset)), n);
360
361                         memcpy(wcp->data + (pos - wcp->offset), data, data_used);
362
363                         /*
364                          * Update the current buffer size with the new data.
365                          */
366
367                         if(pos + data_used > wcp->offset + wcp->data_size) {
368                                 wcp->data_size = pos + data_used - wcp->offset;
369                         }
370
371                         /*
372                          * Update the file size if changed.
373                          */
374
375                         if (wcp->offset + wcp->data_size > wcp->file_size) {
376                                 if (wcp_file_size_change(fsp) == -1) {
377                                         return -1;
378                                 }
379                         }
380
381                         /*
382                          * If we used all the data then
383                          * return here.
384                          */
385
386                         if(n == data_used) {
387                                 return n;
388                         } else {
389                                 cache_flush_needed = True;
390                         }
391                         /*
392                          * Move the start of data forward by the amount used,
393                          * cut down the amount left by the same amount.
394                          */
395
396                         data += data_used;
397                         pos += data_used;
398                         n -= data_used;
399
400                         DO_PROFILE_INC(writecache_abutted_writes);
401                         total_written = data_used;
402
403                         write_path = 1;
404
405                 } else if ((pos < wcp->offset) && (pos + n > wcp->offset) && 
406                                         (pos + n <= wcp->offset + wcp->alloc_size)) {
407
408                         /* ASCII art.... JRA.
409
410                         +---------------+
411                         | Cache buffer  |
412                         +---------------+
413
414             +-------------------+
415             | Data to write     |
416             +-------------------+
417
418                         */
419
420                         /*
421                          * End of write overlaps the existing data.
422                          */
423
424                         size_t data_used = pos + n - wcp->offset;
425
426                         memcpy(wcp->data, data + n - data_used, data_used);
427
428                         /*
429                          * Update the current buffer size with the new data.
430                          */
431
432                         if(pos + n > wcp->offset + wcp->data_size) {
433                                 wcp->data_size = pos + n - wcp->offset;
434                         }
435
436                         /*
437                          * Update the file size if changed.
438                          */
439
440                         if (wcp->offset + wcp->data_size > wcp->file_size) {
441                                 if (wcp_file_size_change(fsp) == -1) {
442                                         return -1;
443                                 }
444                         }
445
446                         /*
447                          * We don't need to move the start of data, but we
448                          * cut down the amount left by the amount used.
449                          */
450
451                         n -= data_used;
452
453                         /*
454                          * We cannot have used all the data here.
455                          */
456
457                         cache_flush_needed = True;
458
459                         DO_PROFILE_INC(writecache_abutted_writes);
460                         total_written = data_used;
461
462                         write_path = 2;
463
464                 } else if ( (pos >= wcp->file_size) && 
465                                         (wcp->offset + wcp->data_size == wcp->file_size) &&
466                                         (pos > wcp->offset + wcp->data_size) && 
467                                         (pos < wcp->offset + wcp->alloc_size) ) {
468
469                         /* ASCII art.... JRA.
470
471                        End of file ---->|
472
473                         +---------------+---------------+
474                         | Cached data   | Cache buffer  |
475                         +---------------+---------------+
476
477                                               +-------------------+
478                                               | Data to write     |
479                                               +-------------------+
480
481                         */
482
483                         /*
484                          * Non-contiguous write part of which fits within
485                          * the cache buffer and is extending the file
486                          * and the cache contents reflect the current
487                          * data up to the current end of the file.
488                          */
489
490                         size_t data_used;
491
492                         if(pos + n <= wcp->offset + wcp->alloc_size) {
493                                 data_used = n;
494                         } else {
495                                 data_used = wcp->offset + wcp->alloc_size - pos;
496                         }
497
498                         /*
499                          * Fill in the non-continuous area with zeros.
500                          */
501
502                         memset(wcp->data + wcp->data_size, '\0',
503                                 pos - (wcp->offset + wcp->data_size) );
504
505                         memcpy(wcp->data + (pos - wcp->offset), data, data_used);
506
507                         /*
508                          * Update the current buffer size with the new data.
509                          */
510
511                         if(pos + data_used > wcp->offset + wcp->data_size) {
512                                 wcp->data_size = pos + data_used - wcp->offset;
513                         }
514
515                         /*
516                          * Update the file size if changed.
517                          */
518
519                         if (wcp->offset + wcp->data_size > wcp->file_size) {
520                                 if (wcp_file_size_change(fsp) == -1) {
521                                         return -1;
522                                 }
523                         }
524
525                         /*
526                          * If we used all the data then
527                          * return here.
528                          */
529
530                         if(n == data_used) {
531                                 return n;
532                         } else {
533                                 cache_flush_needed = True;
534                         }
535
536                         /*
537                          * Move the start of data forward by the amount used,
538                          * cut down the amount left by the same amount.
539                          */
540
541                         data += data_used;
542                         pos += data_used;
543                         n -= data_used;
544
545                         DO_PROFILE_INC(writecache_abutted_writes);
546                         total_written = data_used;
547
548                         write_path = 3;
549
550                 } else if ( (pos >= wcp->file_size) &&
551                             (n == 1) &&
552                             (wcp->file_size == wcp->offset + wcp->data_size) &&
553                             (pos < wcp->file_size + wcp->alloc_size)) {
554
555                         /*
556
557                 End of file ---->|
558
559                  +---------------+---------------+
560                  | Cached data   | Cache buffer  |
561                  +---------------+---------------+
562
563                                  |<------- allocated size ---------------->|
564
565                                                          +--------+
566                                                          | 1 Byte |
567                                                          +--------+
568
569                         MS-Office seems to do this a lot to determine if there's enough
570                         space on the filesystem to write a new file.
571
572                         Change to :
573
574                 End of file ---->|
575                                  +-----------------------+--------+
576                                  | Zeroed Cached data    | 1 Byte |
577                                  +-----------------------+--------+
578                         */
579
580                         flush_write_cache(fsp, WRITE_FLUSH);
581                         wcp->offset = wcp->file_size;
582                         wcp->data_size = pos - wcp->file_size + 1;
583                         memset(wcp->data, '\0', wcp->data_size);
584                         memcpy(wcp->data + wcp->data_size-1, data, 1);
585
586                         /*
587                          * Update the file size if changed.
588                          */
589
590                         if (wcp->offset + wcp->data_size > wcp->file_size) {
591                                 if (wcp_file_size_change(fsp) == -1) {
592                                         return -1;
593                                 }
594                         }
595
596                         return n;
597
598                 } else {
599
600                         /* ASCII art..... JRA.
601
602    Case 1).
603
604                         +---------------+---------------+
605                         | Cached data   | Cache buffer  |
606                         +---------------+---------------+
607
608                                                               +-------------------+
609                                                               | Data to write     |
610                                                               +-------------------+
611
612    Case 2).
613
614                            +---------------+---------------+
615                            | Cached data   | Cache buffer  |
616                            +---------------+---------------+
617
618    +-------------------+
619    | Data to write     |
620    +-------------------+
621
622     Case 3).
623
624                            +---------------+---------------+
625                            | Cached data   | Cache buffer  |
626                            +---------------+---------------+
627
628                   +-----------------------------------------------------+
629                   | Data to write                                       |
630                   +-----------------------------------------------------+
631
632                   */
633
634                         /*
635                          * Write is bigger than buffer, or there is no overlap on the
636                          * low or high ends.
637                          */
638
639                         DEBUG(9,("write_file: non cacheable write : fd = %d, pos = %.0f, len = %u, current cache pos = %.0f \
640 len = %u\n",fsp->fh->fd, (double)pos, (unsigned int)n, (double)wcp->offset, (unsigned int)wcp->data_size ));
641
642                         /*
643                          * If write would fit in the cache, and is larger than
644                          * the data already in the cache, flush the cache and
645                          * preferentially copy the data new data into it. Otherwise
646                          * just write the data directly.
647                          */
648
649                         if ( n <= wcp->alloc_size && n > wcp->data_size) {
650                                 cache_flush_needed = True;
651                         } else {
652                                 ssize_t ret = real_write_file(NULL,fsp, data, pos, n);
653
654                                 /*
655                                  * If the write overlaps the entire cache, then
656                                  * discard the current contents of the cache.
657                                  * Fix from Rasmus Borup Hansen rbh@math.ku.dk.
658                                  */
659
660                                 if ((pos <= wcp->offset) &&
661                                                 (pos + n >= wcp->offset + wcp->data_size) ) {
662                                         DEBUG(9,("write_file: discarding overwritten write \
663 cache: fd = %d, off=%.0f, size=%u\n", fsp->fh->fd, (double)wcp->offset, (unsigned int)wcp->data_size ));
664                                         wcp->data_size = 0;
665                                 }
666
667                                 DO_PROFILE_INC(writecache_direct_writes);
668                                 if (ret == -1) {
669                                         return ret;
670                                 }
671
672                                 if (pos + ret > wcp->file_size) {
673                                         wcp->file_size = pos + ret;
674                                 }
675
676                                 return ret;
677                         }
678
679                         write_path = 4;
680
681                 }
682
683                 if (cache_flush_needed) {
684                         DEBUG(3,("WRITE_FLUSH:%d: due to noncontinuous write: fd = %d, size = %.0f, pos = %.0f, \
685 n = %u, wcp->offset=%.0f, wcp->data_size=%u\n",
686                                 write_path, fsp->fh->fd, (double)wcp->file_size, (double)pos, (unsigned int)n,
687                                 (double)wcp->offset, (unsigned int)wcp->data_size ));
688
689                         flush_write_cache(fsp, WRITE_FLUSH);
690                 }
691         }
692
693         /*
694          * If the write request is bigger than the cache
695          * size, write it all out.
696          */
697
698         if (n > wcp->alloc_size ) {
699                 ssize_t ret = real_write_file(NULL,fsp, data, pos, n);
700                 if (ret == -1) {
701                         return -1;
702                 }
703
704                 if (pos + ret > wcp->file_size) {
705                         wcp->file_size = pos + n;
706                 }
707
708                 DO_PROFILE_INC(writecache_direct_writes);
709                 return total_written + n;
710         }
711
712         /*
713          * If there's any data left, cache it.
714          */
715
716         if (n) {
717 #ifdef WITH_PROFILE
718                 if (wcp->data_size) {
719                         DO_PROFILE_INC(writecache_abutted_writes);
720                 } else {
721                         DO_PROFILE_INC(writecache_init_writes);
722                 }
723 #endif
724                 memcpy(wcp->data+wcp->data_size, data, n);
725                 if (wcp->data_size == 0) {
726                         wcp->offset = pos;
727                         DO_PROFILE_INC(writecache_num_write_caches);
728                 }
729                 wcp->data_size += n;
730
731                 /*
732                  * Update the file size if changed.
733                  */
734
735                 if (wcp->offset + wcp->data_size > wcp->file_size) {
736                         if (wcp_file_size_change(fsp) == -1) {
737                                 return -1;
738                         }
739                 }
740                 DEBUG(9,("wcp->offset = %.0f wcp->data_size = %u cache return %u\n",
741                         (double)wcp->offset, (unsigned int)wcp->data_size, (unsigned int)n));
742
743                 total_written += n;
744                 return total_written; /* .... that's a write :) */
745         }
746   
747         return total_written;
748 }
749
750 /****************************************************************************
751  Delete the write cache structure.
752 ****************************************************************************/
753
754 void delete_write_cache(files_struct *fsp)
755 {
756         write_cache *wcp;
757
758         if(!fsp) {
759                 return;
760         }
761
762         if(!(wcp = fsp->wcp)) {
763                 return;
764         }
765
766         DO_PROFILE_DEC(writecache_allocated_write_caches);
767         allocated_write_caches--;
768
769         SMB_ASSERT(wcp->data_size == 0);
770
771         SAFE_FREE(wcp->data);
772         SAFE_FREE(fsp->wcp);
773
774         DEBUG(10,("delete_write_cache: File %s deleted write cache\n", fsp->fsp_name ));
775 }
776
777 /****************************************************************************
778  Setup the write cache structure.
779 ****************************************************************************/
780
781 static bool setup_write_cache(files_struct *fsp, SMB_OFF_T file_size)
782 {
783         ssize_t alloc_size = lp_write_cache_size(SNUM(fsp->conn));
784         write_cache *wcp;
785
786         if (allocated_write_caches >= MAX_WRITE_CACHES) {
787                 return False;
788         }
789
790         if(alloc_size == 0 || fsp->wcp) {
791                 return False;
792         }
793
794         if((wcp = SMB_MALLOC_P(write_cache)) == NULL) {
795                 DEBUG(0,("setup_write_cache: malloc fail.\n"));
796                 return False;
797         }
798
799         wcp->file_size = file_size;
800         wcp->offset = 0;
801         wcp->alloc_size = alloc_size;
802         wcp->data_size = 0;
803         if((wcp->data = (char *)SMB_MALLOC(wcp->alloc_size)) == NULL) {
804                 DEBUG(0,("setup_write_cache: malloc fail for buffer size %u.\n",
805                         (unsigned int)wcp->alloc_size ));
806                 SAFE_FREE(wcp);
807                 return False;
808         }
809
810         memset(wcp->data, '\0', wcp->alloc_size );
811
812         fsp->wcp = wcp;
813         DO_PROFILE_INC(writecache_allocated_write_caches);
814         allocated_write_caches++;
815
816         DEBUG(10,("setup_write_cache: File %s allocated write cache size %lu\n",
817                 fsp->fsp_name, (unsigned long)wcp->alloc_size ));
818
819         return True;
820 }
821
822 /****************************************************************************
823  Cope with a size change.
824 ****************************************************************************/
825
826 void set_filelen_write_cache(files_struct *fsp, SMB_OFF_T file_size)
827 {
828         if(fsp->wcp) {
829                 /* The cache *must* have been flushed before we do this. */
830                 if (fsp->wcp->data_size != 0) {
831                         char *msg;
832                         asprintf(&msg, "set_filelen_write_cache: size change "
833                                  "on file %s with write cache size = %lu\n",
834                                  fsp->fsp_name,
835                                  (unsigned long)fsp->wcp->data_size);
836                         smb_panic(msg);
837                 }
838                 fsp->wcp->file_size = file_size;
839         }
840 }
841
842 /*******************************************************************
843  Flush a write cache struct to disk.
844 ********************************************************************/
845
846 ssize_t flush_write_cache(files_struct *fsp, enum flush_reason_enum reason)
847 {
848         write_cache *wcp = fsp->wcp;
849         size_t data_size;
850         ssize_t ret;
851
852         if(!wcp || !wcp->data_size) {
853                 return 0;
854         }
855
856         data_size = wcp->data_size;
857         wcp->data_size = 0;
858
859         DO_PROFILE_DEC_INC(writecache_num_write_caches,writecache_flushed_writes[reason]);
860
861         DEBUG(9,("flushing write cache: fd = %d, off=%.0f, size=%u\n",
862                 fsp->fh->fd, (double)wcp->offset, (unsigned int)data_size));
863
864 #ifdef WITH_PROFILE
865         if(data_size == wcp->alloc_size) {
866                 DO_PROFILE_INC(writecache_num_perfect_writes);
867         }
868 #endif
869
870         ret = real_write_file(NULL, fsp, wcp->data, wcp->offset, data_size);
871
872         /*
873          * Ensure file size if kept up to date if write extends file.
874          */
875
876         if ((ret != -1) && (wcp->offset + ret > wcp->file_size)) {
877                 wcp->file_size = wcp->offset + ret;
878         }
879
880         return ret;
881 }
882
883 /*******************************************************************
884 sync a file
885 ********************************************************************/
886
887 NTSTATUS sync_file(connection_struct *conn, files_struct *fsp, bool write_through)
888 {
889         if (fsp->fh->fd == -1)
890                 return NT_STATUS_INVALID_HANDLE;
891
892         if (lp_strict_sync(SNUM(conn)) &&
893             (lp_syncalways(SNUM(conn)) || write_through)) {
894                 int ret = flush_write_cache(fsp, SYNC_FLUSH);
895                 if (ret == -1) {
896                         return map_nt_error_from_unix(errno);
897                 }
898                 ret = SMB_VFS_FSYNC(fsp);
899                 if (ret == -1) {
900                         return map_nt_error_from_unix(errno);
901                 }
902         }
903         return NT_STATUS_OK;
904 }
905
906 /************************************************************
907  Perform a stat whether a valid fd or not.
908 ************************************************************/
909
910 int fsp_stat(files_struct *fsp, SMB_STRUCT_STAT *pst)
911 {
912         if (fsp->fh->fd == -1) {
913                 return SMB_VFS_STAT(fsp->conn, fsp->fsp_name, pst);
914         } else {
915                 return SMB_VFS_FSTAT(fsp, pst);
916         }
917 }