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