Removed 'extern int DEBUGLEVEL' as it is now in the smb.h header.
[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    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23
24 static BOOL setup_write_cache(files_struct *, SMB_OFF_T);
25
26 /****************************************************************************
27 seek a file. Try to avoid the seek if possible
28 ****************************************************************************/
29
30 SMB_OFF_T seek_file(files_struct *fsp,SMB_OFF_T pos)
31 {
32   SMB_OFF_T offset = 0;
33   SMB_OFF_T seek_ret;
34
35   if (fsp->print_file && lp_postscript(fsp->conn->service))
36     offset = 3;
37
38   seek_ret = fsp->conn->vfs_ops.lseek(fsp,fsp->fd,pos+offset,SEEK_SET);
39
40   /*
41    * We want to maintain the fiction that we can seek
42    * on a fifo for file system purposes. This allows 
43    * people to set up UNIX fifo's that feed data to Windows
44    * applications. JRA.
45    */
46
47   if((seek_ret == -1) && (errno == ESPIPE)) {
48     seek_ret = pos+offset;
49     errno = 0;
50   }
51
52   if((seek_ret == -1) || (seek_ret != pos+offset)) {
53     DEBUG(0,("seek_file: sys_lseek failed. Error was %s\n", strerror(errno) ));
54     fsp->pos = -1;
55     return -1;
56   }
57
58   fsp->pos = seek_ret - offset;
59
60   DEBUG(10,("seek_file: requested pos = %.0f, new pos = %.0f\n",
61         (double)(pos+offset), (double)fsp->pos ));
62
63   return(fsp->pos);
64 }
65
66 /****************************************************************************
67  Read from write cache if we can.
68 ****************************************************************************/
69
70
71 BOOL read_from_write_cache(files_struct *fsp,char *data,SMB_OFF_T pos,size_t n)
72 {
73   write_cache *wcp = fsp->wcp;
74
75   if(!wcp)
76     return False;
77
78   if(n > wcp->data_size || pos < wcp->offset || pos + n > wcp->offset + wcp->data_size)
79     return False;
80
81   memcpy(data, wcp->data + (pos - wcp->offset), n);
82
83   DO_PROFILE_INC(writecache_read_hits);
84
85   return True;
86 }
87
88 /****************************************************************************
89 read from a file
90 ****************************************************************************/
91
92 ssize_t read_file(files_struct *fsp,char *data,SMB_OFF_T pos,size_t n)
93 {
94   ssize_t ret=0,readret;
95
96   /* you can't read from print files */
97   if (fsp->print_file) {
98           return -1;
99   }
100
101   /*
102    * Serve from write cache if we can.
103    */
104   if(read_from_write_cache(fsp, data, pos, n))
105     return n;
106
107   flush_write_cache(fsp, READ_FLUSH);
108
109   if (seek_file(fsp,pos) == -1) {
110     DEBUG(3,("read_file: Failed to seek to %.0f\n",(double)pos));
111     return(ret);
112   }
113   
114   if (n > 0) {
115     readret = fsp->conn->vfs_ops.read(fsp,fsp->fd,data,n);
116     if (readret == -1)
117       return -1;
118     if (readret > 0) ret += readret;
119   }
120
121   return(ret);
122 }
123
124 /* how many write cache buffers have been allocated */
125 static unsigned int allocated_write_caches;
126
127 /****************************************************************************
128  *Really* write to a file.
129 ****************************************************************************/
130
131 static ssize_t real_write_file(files_struct *fsp,char *data,SMB_OFF_T pos, size_t n)
132 {
133   if ((pos != -1) && (seek_file(fsp,pos) == -1))
134     return -1;
135
136   return vfs_write_data(fsp,data,n);
137 }
138
139 /****************************************************************************
140 write to a file
141 ****************************************************************************/
142
143 ssize_t write_file(files_struct *fsp, char *data, SMB_OFF_T pos, size_t n)
144 {
145   write_cache *wcp = fsp->wcp;
146   ssize_t total_written = 0;
147   int write_path = -1; 
148
149   if (fsp->print_file) {
150           return print_job_write(fsp->print_jobid, data, n);
151   }
152
153   if (!fsp->can_write) {
154     errno = EPERM;
155     return(0);
156   }
157
158   if (!fsp->modified) {
159     SMB_STRUCT_STAT st;
160     fsp->modified = True;
161
162     if (fsp->conn->vfs_ops.fstat(fsp,fsp->fd,&st) == 0) {
163       int dosmode = dos_mode(fsp->conn,fsp->fsp_name,&st);
164       if (MAP_ARCHIVE(fsp->conn) && !IS_DOS_ARCHIVE(dosmode)) { 
165         file_chmod(fsp->conn,fsp->fsp_name,dosmode | aARCH,&st);
166       }
167
168       /*
169        * If this is the first write and we have an exclusive oplock then setup
170        * the write cache.
171        */
172
173       if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type) && !wcp) {
174         setup_write_cache(fsp, st.st_size);
175         wcp = fsp->wcp;
176       } 
177     }  
178   }
179
180 #ifdef WITH_PROFILE
181   DO_PROFILE_INC(writecache_total_writes);
182   if (!fsp->oplock_type) {
183     DO_PROFILE_INC(writecache_non_oplock_writes);
184   }
185 #endif
186
187   /*
188    * If this file is level II oplocked then we need
189    * to grab the shared memory lock and inform all
190    * other files with a level II lock that they need
191    * to flush their read caches. We keep the lock over
192    * the shared memory area whilst doing this.
193    */
194
195   release_level_2_oplocks_on_change(fsp);
196
197 #ifdef WITH_PROFILE
198   if (profile_p && profile_p->writecache_total_writes % 500 == 0) {
199     DEBUG(3,("WRITECACHE: initwrites=%u abutted=%u total=%u \
200 nonop=%u allocated=%u active=%u direct=%u perfect=%u readhits=%u\n",
201         profile_p->writecache_init_writes,
202         profile_p->writecache_abutted_writes,
203         profile_p->writecache_total_writes,
204         profile_p->writecache_non_oplock_writes,
205         profile_p->writecache_allocated_write_caches,
206         profile_p->writecache_num_write_caches,
207         profile_p->writecache_direct_writes,
208         profile_p->writecache_num_perfect_writes,
209         profile_p->writecache_read_hits ));
210
211     DEBUG(3,("WRITECACHE: Flushes SEEK=%d, READ=%d, WRITE=%d, READRAW=%d, OPLOCK=%d, CLOSE=%d, SYNC=%d\n",
212         profile_p->writecache_flushed_writes[SEEK_FLUSH],
213         profile_p->writecache_flushed_writes[READ_FLUSH],
214         profile_p->writecache_flushed_writes[WRITE_FLUSH],
215         profile_p->writecache_flushed_writes[READRAW_FLUSH],
216         profile_p->writecache_flushed_writes[OPLOCK_RELEASE_FLUSH],
217         profile_p->writecache_flushed_writes[CLOSE_FLUSH],
218         profile_p->writecache_flushed_writes[SYNC_FLUSH] ));
219   }
220 #endif
221
222   if(!wcp) {
223     DO_PROFILE_INC(writecache_direct_writes);
224     return real_write_file(fsp, data, pos, n);
225   }
226
227   DEBUG(9,("write_file(fd=%d pos=%d size=%d) wofs=%d wsize=%d\n",
228            fsp->fd, (int)pos, (int)n, (int)wcp->offset, (int)wcp->data_size));
229
230   /* 
231    * If we have active cache and it isn't contiguous then we flush.
232    * NOTE: There is a small problem with running out of disk ....
233    */
234
235   if (wcp->data_size) {
236
237     BOOL cache_flush_needed = False;
238
239     if ((pos >= wcp->offset) && (pos <= wcp->offset + wcp->data_size)) {
240       
241       /*
242        * Start of write overlaps or abutts the existing data.
243        */
244
245       size_t data_used = MIN((wcp->alloc_size - (pos - wcp->offset)), n);
246
247       memcpy(wcp->data + (pos - wcp->offset), data, data_used);
248
249       /*
250        * Update the current buffer size with the new data.
251        */
252
253       if(pos + data_used > wcp->offset + wcp->data_size)
254         wcp->data_size = pos + data_used - wcp->offset;
255
256       /*
257        * If we used all the data then
258        * return here.
259        */
260
261       if(n == data_used)
262         return n;
263       else
264         cache_flush_needed = True;
265
266       /*
267        * Move the start of data forward by the amount used,
268        * cut down the amount left by the same amount.
269        */
270
271       data += data_used;
272       pos += data_used;
273       n -= data_used;
274
275       DO_PROFILE_INC(writecache_abutted_writes);
276       total_written = data_used;
277
278       write_path = 1;
279
280     } else if ((pos < wcp->offset) && (pos + n > wcp->offset) && 
281                (pos + n <= wcp->offset + wcp->alloc_size)) {
282
283       /*
284        * End of write overlaps the existing data.
285        */
286
287       size_t data_used = pos + n - wcp->offset;
288
289       memcpy(wcp->data, data + n - data_used, data_used);
290
291       /*
292        * Update the current buffer size with the new data.
293        */
294
295       if(pos + n > wcp->offset + wcp->data_size)
296         wcp->data_size = pos + n - wcp->offset;
297
298       /*
299        * We don't need to move the start of data, but we
300        * cut down the amount left by the amount used.
301        */
302
303       n -= data_used;
304
305       /*
306        * We cannot have used all the data here.
307        */
308
309       cache_flush_needed = True;
310
311       DO_PROFILE_INC(writecache_abutted_writes);
312       total_written = data_used;
313
314       write_path = 2;
315
316     } else if ( (pos >= wcp->file_size) && 
317                 (pos > wcp->offset + wcp->data_size) && 
318                 (pos < wcp->offset + wcp->alloc_size) ) {
319
320       /*
321        * Non-contiguous write part of which fits within
322        * the cache buffer and is extending the file.
323        */
324
325       size_t data_used;
326
327       if(pos + n <= wcp->offset + wcp->alloc_size)
328         data_used = n;
329       else
330         data_used = wcp->offset + wcp->alloc_size - pos;
331
332       /*
333        * Fill in the non-continuous area with zeros.
334        */
335
336       memset(wcp->data + wcp->data_size, '\0',
337              pos - (wcp->offset + wcp->data_size) );
338
339       memcpy(wcp->data + (pos - wcp->offset), data, data_used);
340
341       /*
342        * Update the current buffer size with the new data.
343        */
344
345       if(pos + data_used > wcp->offset + wcp->data_size)
346         wcp->data_size = pos + data_used - wcp->offset;
347
348       /*
349        * Update the known file length.
350        */
351
352       wcp->file_size = wcp->offset + wcp->data_size;
353
354       /*
355        * If we used all the data then
356        * return here.
357        */
358
359       if(n == data_used)
360         return n;
361       else
362         cache_flush_needed = True;
363
364       /*
365        * Move the start of data forward by the amount used,
366        * cut down the amount left by the same amount.
367        */
368
369       data += data_used;
370       pos += data_used;
371       n -= data_used;
372
373       DO_PROFILE_INC(writecache_abutted_writes);
374       total_written = data_used;
375
376       write_path = 3;
377
378     } else {
379
380       /*
381        * Write is bigger than buffer, or there is no overlap on the
382        * low or high ends.
383        */
384
385       DEBUG(9,("write_file: non cacheable write : fd = %d, pos = %.0f, len = %u, current cache pos = %.0f \
386 len = %u\n",fsp->fd, (double)pos, (unsigned int)n, (double)wcp->offset, (unsigned int)wcp->data_size ));
387
388       /*
389        * Update the file size if needed.
390        */
391
392       if(pos + n > wcp->file_size)
393         wcp->file_size = pos + n;
394
395       /*
396        * If write would fit in the cache, and is larger than
397        * the data already in the cache, flush the cache and
398        * preferentially copy the data new data into it. Otherwise
399        * just write the data directly.
400        */
401
402       if ( n <= wcp->alloc_size && n > wcp->data_size) {
403         cache_flush_needed = True;
404       } else {
405         DO_PROFILE_INC(writecache_direct_writes);
406         return real_write_file(fsp, data, pos, n);
407       }
408
409       write_path = 4;
410
411     }
412
413     if(wcp->data_size > wcp->file_size)
414       wcp->file_size = wcp->data_size;
415
416     if (cache_flush_needed) {
417       DEBUG(3,("WRITE_FLUSH:%d: due to noncontinuous write: fd = %d, size = %.0f, pos = %.0f, \
418 n = %u, wcp->offset=%.0f, wcp->data_size=%u\n",
419              write_path, fsp->fd, (double)wcp->file_size, (double)pos, (unsigned int)n,
420              (double)wcp->offset, (unsigned int)wcp->data_size ));
421
422       flush_write_cache(fsp, WRITE_FLUSH);
423     }
424   }
425
426   /*
427    * If the write request is bigger than the cache
428    * size, write it all out.
429    */
430
431   if (n > wcp->alloc_size ) {
432     if(real_write_file(fsp, data, pos, n) == -1)
433       return -1;
434     DO_PROFILE_INC(writecache_direct_writes);
435     return total_written + n;
436   }
437
438   /*
439    * If there's any data left, cache it.
440    */
441
442   if (n) {
443 #ifdef WITH_PROFILE
444     if (wcp->data_size) {
445       DO_PROFILE_INC(writecache_abutted_writes);
446     } else {
447       DO_PROFILE_INC(writecache_init_writes);
448     }
449 #endif
450     memcpy(wcp->data+wcp->data_size, data, n);
451     if (wcp->data_size == 0) {
452       wcp->offset = pos;
453       DO_PROFILE_INC(writecache_num_write_caches);
454     }
455     wcp->data_size += n;
456     DEBUG(9,("cache return %u\n", (unsigned int)n));
457     total_written += n;
458     return total_written; /* .... that's a write :) */
459   }
460   
461   return total_written;
462 }
463
464 /****************************************************************************
465  Delete the write cache structure.
466 ****************************************************************************/
467
468 void delete_write_cache(files_struct *fsp)
469 {
470   write_cache *wcp;
471
472   if(!fsp)
473     return;
474
475   if(!(wcp = fsp->wcp))
476     return;
477
478   DO_PROFILE_DEC(writecache_allocated_write_caches);
479   allocated_write_caches--;
480
481   SMB_ASSERT(wcp->data_size == 0);
482
483   SAFE_FREE(wcp->data);
484   SAFE_FREE(fsp->wcp);
485
486   DEBUG(10,("delete_write_cache: File %s deleted write cache\n", fsp->fsp_name ));
487
488 }
489
490 /****************************************************************************
491  Setup the write cache structure.
492 ****************************************************************************/
493
494 static BOOL setup_write_cache(files_struct *fsp, SMB_OFF_T file_size)
495 {
496   ssize_t alloc_size = lp_write_cache_size(SNUM(fsp->conn));
497   write_cache *wcp;
498
499   if (allocated_write_caches >= MAX_WRITE_CACHES) 
500         return False;
501
502   if(alloc_size == 0 || fsp->wcp)
503     return False;
504
505   if((wcp = (write_cache *)malloc(sizeof(write_cache))) == NULL) {
506     DEBUG(0,("setup_write_cache: malloc fail.\n"));
507     return False;
508   }
509
510   wcp->file_size = file_size;
511   wcp->offset = 0;
512   wcp->alloc_size = alloc_size;
513   wcp->data_size = 0;
514   if((wcp->data = malloc(wcp->alloc_size)) == NULL) {
515     DEBUG(0,("setup_write_cache: malloc fail for buffer size %u.\n",
516           (unsigned int)wcp->alloc_size ));
517     SAFE_FREE(wcp);
518     return False;
519   }
520
521   fsp->wcp = wcp;
522   DO_PROFILE_INC(writecache_allocated_write_caches);
523   allocated_write_caches++;
524
525   DEBUG(10,("setup_write_cache: File %s allocated write cache size %u\n",
526                 fsp->fsp_name, wcp->alloc_size ));
527
528   return True;
529 }
530
531 /****************************************************************************
532  Cope with a size change.
533 ****************************************************************************/
534
535 void set_filelen_write_cache(files_struct *fsp, SMB_OFF_T file_size)
536 {
537   if(fsp->wcp) {
538     flush_write_cache(fsp, SIZECHANGE_FLUSH);
539     fsp->wcp->file_size = file_size;
540   }
541 }
542
543 /*******************************************************************
544  Flush a write cache struct to disk.
545 ********************************************************************/
546
547 ssize_t flush_write_cache(files_struct *fsp, enum flush_reason_enum reason)
548 {
549   write_cache *wcp = fsp->wcp;
550   size_t data_size;
551
552   if(!wcp || !wcp->data_size)
553     return 0;
554
555   data_size = wcp->data_size;
556   wcp->data_size = 0;
557
558   DO_PROFILE_DEC_INC(writecache_num_write_caches,writecache_flushed_writes[reason]);
559
560   DEBUG(9,("flushing write cache: fd = %d, off=%.0f, size=%u\n",
561            fsp->fd, (double)wcp->offset, (unsigned int)data_size));
562
563 #ifdef WITH_PROFILE
564   if(data_size == wcp->alloc_size)
565     DO_PROFILE_INC(writecache_num_perfect_writes);
566 #endif
567
568   return real_write_file(fsp, wcp->data, wcp->offset, data_size);
569 }
570
571 /*******************************************************************
572 sync a file
573 ********************************************************************/
574
575 void sync_file(connection_struct *conn, files_struct *fsp)
576 {
577     if(lp_strict_sync(SNUM(conn)) && fsp->fd != -1) {
578       flush_write_cache(fsp, SYNC_FLUSH);
579       conn->vfs_ops.fsync(fsp,fsp->fd);
580     }
581 }