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