r23784: use the GPLv3 boilerplate as recommended by the FSF and the license text
[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                             (pos < wcp->offset + 2*wcp->alloc_size) &&
514                             (wcp->file_size == wcp->offset + wcp->data_size)) {
515
516                         /*
517                         +---------------+
518                         | Cached data   |
519                         +---------------+
520
521                                                          +--------+
522                                                          | 1 Byte |
523                                                          +--------+
524
525                         MS-Office seems to do this a lot to determine if there's enough
526                         space on the filesystem to write a new file.
527                         */
528
529                         SMB_BIG_UINT new_start = wcp->offset + wcp->data_size;
530
531                         flush_write_cache(fsp, WRITE_FLUSH);
532                         wcp->offset = new_start;
533                         wcp->data_size = pos - new_start + 1;
534                         memset(wcp->data, '\0', wcp->data_size);
535                         memcpy(wcp->data + wcp->data_size-1, data, 1);
536
537                         /*
538                          * Update the file size if changed.
539                          */
540
541                         if (wcp->offset + wcp->data_size > wcp->file_size) {
542                                 if (wcp_file_size_change(fsp) == -1) {
543                                         return -1;
544                                 }
545                         }
546
547                         return n;
548
549                 } else {
550
551                         /* ASCII art..... JRA.
552
553    Case 1).
554
555                         +---------------+---------------+
556                         | Cached data   | Cache buffer  |
557                         +---------------+---------------+
558
559                                                               +-------------------+
560                                                               | Data to write     |
561                                                               +-------------------+
562
563    Case 2).
564
565                            +---------------+---------------+
566                            | Cached data   | Cache buffer  |
567                            +---------------+---------------+
568
569    +-------------------+
570    | Data to write     |
571    +-------------------+
572
573     Case 3).
574
575                            +---------------+---------------+
576                            | Cached data   | Cache buffer  |
577                            +---------------+---------------+
578
579                   +-----------------------------------------------------+
580                   | Data to write                                       |
581                   +-----------------------------------------------------+
582
583                   */
584
585                         /*
586                          * Write is bigger than buffer, or there is no overlap on the
587                          * low or high ends.
588                          */
589
590                         DEBUG(9,("write_file: non cacheable write : fd = %d, pos = %.0f, len = %u, current cache pos = %.0f \
591 len = %u\n",fsp->fh->fd, (double)pos, (unsigned int)n, (double)wcp->offset, (unsigned int)wcp->data_size ));
592
593                         /*
594                          * If write would fit in the cache, and is larger than
595                          * the data already in the cache, flush the cache and
596                          * preferentially copy the data new data into it. Otherwise
597                          * just write the data directly.
598                          */
599
600                         if ( n <= wcp->alloc_size && n > wcp->data_size) {
601                                 cache_flush_needed = True;
602                         } else {
603                                 ssize_t ret = real_write_file(fsp, data, pos, n);
604
605                                 /*
606                                  * If the write overlaps the entire cache, then
607                                  * discard the current contents of the cache.
608                                  * Fix from Rasmus Borup Hansen rbh@math.ku.dk.
609                                  */
610
611                                 if ((pos <= wcp->offset) &&
612                                                 (pos + n >= wcp->offset + wcp->data_size) ) {
613                                         DEBUG(9,("write_file: discarding overwritten write \
614 cache: fd = %d, off=%.0f, size=%u\n", fsp->fh->fd, (double)wcp->offset, (unsigned int)wcp->data_size ));
615                                         wcp->data_size = 0;
616                                 }
617
618                                 DO_PROFILE_INC(writecache_direct_writes);
619                                 if (ret == -1) {
620                                         return ret;
621                                 }
622
623                                 if (pos + ret > wcp->file_size) {
624                                         wcp->file_size = pos + ret;
625                                 }
626
627                                 return ret;
628                         }
629
630                         write_path = 4;
631
632                 }
633
634                 if (cache_flush_needed) {
635                         DEBUG(3,("WRITE_FLUSH:%d: due to noncontinuous write: fd = %d, size = %.0f, pos = %.0f, \
636 n = %u, wcp->offset=%.0f, wcp->data_size=%u\n",
637                                 write_path, fsp->fh->fd, (double)wcp->file_size, (double)pos, (unsigned int)n,
638                                 (double)wcp->offset, (unsigned int)wcp->data_size ));
639
640                         flush_write_cache(fsp, WRITE_FLUSH);
641                 }
642         }
643
644         /*
645          * If the write request is bigger than the cache
646          * size, write it all out.
647          */
648
649         if (n > wcp->alloc_size ) {
650                 ssize_t ret = real_write_file(fsp, data, pos, n);
651                 if (ret == -1) {
652                         return -1;
653                 }
654
655                 if (pos + ret > wcp->file_size) {
656                         wcp->file_size = pos + n;
657                 }
658
659                 DO_PROFILE_INC(writecache_direct_writes);
660                 return total_written + n;
661         }
662
663         /*
664          * If there's any data left, cache it.
665          */
666
667         if (n) {
668 #ifdef WITH_PROFILE
669                 if (wcp->data_size) {
670                         DO_PROFILE_INC(writecache_abutted_writes);
671                 } else {
672                         DO_PROFILE_INC(writecache_init_writes);
673                 }
674 #endif
675                 memcpy(wcp->data+wcp->data_size, data, n);
676                 if (wcp->data_size == 0) {
677                         wcp->offset = pos;
678                         DO_PROFILE_INC(writecache_num_write_caches);
679                 }
680                 wcp->data_size += n;
681
682                 /*
683                  * Update the file size if changed.
684                  */
685
686                 if (wcp->offset + wcp->data_size > wcp->file_size) {
687                         if (wcp_file_size_change(fsp) == -1) {
688                                 return -1;
689                         }
690                 }
691                 DEBUG(9,("wcp->offset = %.0f wcp->data_size = %u cache return %u\n",
692                         (double)wcp->offset, (unsigned int)wcp->data_size, (unsigned int)n));
693
694                 total_written += n;
695                 return total_written; /* .... that's a write :) */
696         }
697   
698         return total_written;
699 }
700
701 /****************************************************************************
702  Delete the write cache structure.
703 ****************************************************************************/
704
705 void delete_write_cache(files_struct *fsp)
706 {
707         write_cache *wcp;
708
709         if(!fsp) {
710                 return;
711         }
712
713         if(!(wcp = fsp->wcp)) {
714                 return;
715         }
716
717         DO_PROFILE_DEC(writecache_allocated_write_caches);
718         allocated_write_caches--;
719
720         SMB_ASSERT(wcp->data_size == 0);
721
722         SAFE_FREE(wcp->data);
723         SAFE_FREE(fsp->wcp);
724
725         DEBUG(10,("delete_write_cache: File %s deleted write cache\n", fsp->fsp_name ));
726 }
727
728 /****************************************************************************
729  Setup the write cache structure.
730 ****************************************************************************/
731
732 static BOOL setup_write_cache(files_struct *fsp, SMB_OFF_T file_size)
733 {
734         ssize_t alloc_size = lp_write_cache_size(SNUM(fsp->conn));
735         write_cache *wcp;
736
737         if (allocated_write_caches >= MAX_WRITE_CACHES) {
738                 return False;
739         }
740
741         if(alloc_size == 0 || fsp->wcp) {
742                 return False;
743         }
744
745         if((wcp = SMB_MALLOC_P(write_cache)) == NULL) {
746                 DEBUG(0,("setup_write_cache: malloc fail.\n"));
747                 return False;
748         }
749
750         wcp->file_size = file_size;
751         wcp->offset = 0;
752         wcp->alloc_size = alloc_size;
753         wcp->data_size = 0;
754         if((wcp->data = (char *)SMB_MALLOC(wcp->alloc_size)) == NULL) {
755                 DEBUG(0,("setup_write_cache: malloc fail for buffer size %u.\n",
756                         (unsigned int)wcp->alloc_size ));
757                 SAFE_FREE(wcp);
758                 return False;
759         }
760
761         memset(wcp->data, '\0', wcp->alloc_size );
762
763         fsp->wcp = wcp;
764         DO_PROFILE_INC(writecache_allocated_write_caches);
765         allocated_write_caches++;
766
767         DEBUG(10,("setup_write_cache: File %s allocated write cache size %lu\n",
768                 fsp->fsp_name, (unsigned long)wcp->alloc_size ));
769
770         return True;
771 }
772
773 /****************************************************************************
774  Cope with a size change.
775 ****************************************************************************/
776
777 void set_filelen_write_cache(files_struct *fsp, SMB_OFF_T file_size)
778 {
779         if(fsp->wcp) {
780                 /* The cache *must* have been flushed before we do this. */
781                 if (fsp->wcp->data_size != 0) {
782                         char *msg;
783                         asprintf(&msg, "set_filelen_write_cache: size change "
784                                  "on file %s with write cache size = %lu\n",
785                                  fsp->fsp_name,
786                                  (unsigned long)fsp->wcp->data_size);
787                         smb_panic(msg);
788                 }
789                 fsp->wcp->file_size = file_size;
790         }
791 }
792
793 /*******************************************************************
794  Flush a write cache struct to disk.
795 ********************************************************************/
796
797 ssize_t flush_write_cache(files_struct *fsp, enum flush_reason_enum reason)
798 {
799         write_cache *wcp = fsp->wcp;
800         size_t data_size;
801         ssize_t ret;
802
803         if(!wcp || !wcp->data_size) {
804                 return 0;
805         }
806
807         data_size = wcp->data_size;
808         wcp->data_size = 0;
809
810         DO_PROFILE_DEC_INC(writecache_num_write_caches,writecache_flushed_writes[reason]);
811
812         DEBUG(9,("flushing write cache: fd = %d, off=%.0f, size=%u\n",
813                 fsp->fh->fd, (double)wcp->offset, (unsigned int)data_size));
814
815 #ifdef WITH_PROFILE
816         if(data_size == wcp->alloc_size) {
817                 DO_PROFILE_INC(writecache_num_perfect_writes);
818         }
819 #endif
820
821         ret = real_write_file(fsp, wcp->data, wcp->offset, data_size);
822
823         /*
824          * Ensure file size if kept up to date if write extends file.
825          */
826
827         if ((ret != -1) && (wcp->offset + ret > wcp->file_size)) {
828                 wcp->file_size = wcp->offset + ret;
829         }
830
831         return ret;
832 }
833
834 /*******************************************************************
835 sync a file
836 ********************************************************************/
837
838 NTSTATUS sync_file(connection_struct *conn, files_struct *fsp, BOOL write_through)
839 {
840         if (fsp->fh->fd == -1)
841                 return NT_STATUS_INVALID_HANDLE;
842
843         if (lp_strict_sync(SNUM(conn)) &&
844             (lp_syncalways(SNUM(conn)) || write_through)) {
845                 int ret = flush_write_cache(fsp, SYNC_FLUSH);
846                 if (ret == -1) {
847                         return map_nt_error_from_unix(errno);
848                 }
849                 ret = SMB_VFS_FSYNC(fsp,fsp->fh->fd);
850                 if (ret == -1) {
851                         return map_nt_error_from_unix(errno);
852                 }
853         }
854         return NT_STATUS_OK;
855 }
856
857 /************************************************************
858  Perform a stat whether a valid fd or not.
859 ************************************************************/
860
861 int fsp_stat(files_struct *fsp, SMB_STRUCT_STAT *pst)
862 {
863         if (fsp->fh->fd == -1) {
864                 return SMB_VFS_STAT(fsp->conn, fsp->fsp_name, pst);
865         } else {
866                 return SMB_VFS_FSTAT(fsp,fsp->fh->fd, pst);
867         }
868 }