00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "includes.h"
00024
00025 static BOOL setup_write_cache(files_struct *, SMB_OFF_T);
00026
00027
00028
00029
00030
00031 static BOOL read_from_write_cache(files_struct *fsp,char *data,SMB_OFF_T pos,size_t n)
00032 {
00033 write_cache *wcp = fsp->wcp;
00034
00035 if(!wcp) {
00036 return False;
00037 }
00038
00039 if( n > wcp->data_size || pos < wcp->offset || pos + n > wcp->offset + wcp->data_size) {
00040 return False;
00041 }
00042
00043 memcpy(data, wcp->data + (pos - wcp->offset), n);
00044
00045 DO_PROFILE_INC(writecache_read_hits);
00046
00047 return True;
00048 }
00049
00050
00051
00052
00053
00054 ssize_t read_file(files_struct *fsp,char *data,SMB_OFF_T pos,size_t n)
00055 {
00056 ssize_t ret=0,readret;
00057
00058
00059 if (fsp->print_file) {
00060 return -1;
00061 }
00062
00063
00064
00065
00066
00067 if(read_from_write_cache(fsp, data, pos, n)) {
00068 fsp->fh->pos = pos + n;
00069 fsp->fh->position_information = fsp->fh->pos;
00070 return n;
00071 }
00072
00073 flush_write_cache(fsp, READ_FLUSH);
00074
00075 fsp->fh->pos = pos;
00076
00077 if (n > 0) {
00078 #ifdef DMF_FIX
00079 int numretries = 3;
00080 tryagain:
00081 readret = SMB_VFS_PREAD(fsp,fsp->fh->fd,data,n,pos);
00082
00083 if (readret == -1) {
00084 if ((errno == EAGAIN) && numretries) {
00085 DEBUG(3,("read_file EAGAIN retry in 10 seconds\n"));
00086 (void)sleep(10);
00087 --numretries;
00088 goto tryagain;
00089 }
00090 return -1;
00091 }
00092 #else
00093 readret = SMB_VFS_PREAD(fsp,fsp->fh->fd,data,n,pos);
00094
00095 if (readret == -1) {
00096 return -1;
00097 }
00098 #endif
00099 if (readret > 0) {
00100 ret += readret;
00101 }
00102 }
00103
00104 DEBUG(10,("read_file (%s): pos = %.0f, size = %lu, returned %lu\n",
00105 fsp->fsp_name, (double)pos, (unsigned long)n, (long)ret ));
00106
00107 fsp->fh->pos += ret;
00108 fsp->fh->position_information = fsp->fh->pos;
00109
00110 return(ret);
00111 }
00112
00113
00114 static unsigned int allocated_write_caches;
00115
00116
00117
00118
00119
00120 static ssize_t real_write_file(files_struct *fsp,const char *data, SMB_OFF_T pos, size_t n)
00121 {
00122 ssize_t ret;
00123
00124 if (pos == -1) {
00125 ret = vfs_write_data(fsp, data, n);
00126 } else {
00127 fsp->fh->pos = pos;
00128 if (pos && lp_strict_allocate(SNUM(fsp->conn))) {
00129 if (vfs_fill_sparse(fsp, pos) == -1) {
00130 return -1;
00131 }
00132 }
00133 ret = vfs_pwrite_data(fsp, data, n, pos);
00134 }
00135
00136 DEBUG(10,("real_write_file (%s): pos = %.0f, size = %lu, returned %ld\n",
00137 fsp->fsp_name, (double)pos, (unsigned long)n, (long)ret ));
00138
00139 if (ret != -1) {
00140 fsp->fh->pos += ret;
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152 if (!null_timespec(fsp->pending_modtime)) {
00153 set_filetime(fsp->conn, fsp->fsp_name, fsp->pending_modtime);
00154
00155
00156
00157 if (!fsp->pending_modtime_owner) {
00158 fsp->last_write_time = timespec_current();
00159 }
00160 }
00161
00162
00163
00164 #if 0
00165 fsp->position_information = fsp->pos;
00166 #endif
00167 }
00168
00169 return ret;
00170 }
00171
00172
00173
00174
00175
00176
00177 static int wcp_file_size_change(files_struct *fsp)
00178 {
00179 int ret;
00180 write_cache *wcp = fsp->wcp;
00181
00182 wcp->file_size = wcp->offset + wcp->data_size;
00183 ret = SMB_VFS_FTRUNCATE(fsp, fsp->fh->fd, wcp->file_size);
00184 if (ret == -1) {
00185 DEBUG(0,("wcp_file_size_change (%s): ftruncate of size %.0f error %s\n",
00186 fsp->fsp_name, (double)wcp->file_size, strerror(errno) ));
00187 }
00188 return ret;
00189 }
00190
00191
00192
00193
00194
00195 ssize_t write_file(files_struct *fsp, const char *data, SMB_OFF_T pos, size_t n)
00196 {
00197 write_cache *wcp = fsp->wcp;
00198 ssize_t total_written = 0;
00199 int write_path = -1;
00200
00201 if (fsp->print_file) {
00202 fstring sharename;
00203 uint32 jobid;
00204
00205 if (!rap_to_pjobid(fsp->rap_print_jobid, sharename, &jobid)) {
00206 DEBUG(3,("write_file: Unable to map RAP jobid %u to jobid.\n",
00207 (unsigned int)fsp->rap_print_jobid ));
00208 errno = EBADF;
00209 return -1;
00210 }
00211
00212 return print_job_write(SNUM(fsp->conn), jobid, data, pos, n);
00213 }
00214
00215 if (!fsp->can_write) {
00216 errno = EPERM;
00217 return -1;
00218 }
00219
00220 if (!fsp->modified) {
00221 SMB_STRUCT_STAT st;
00222 fsp->modified = True;
00223
00224 if (SMB_VFS_FSTAT(fsp,fsp->fh->fd,&st) == 0) {
00225 int dosmode = dos_mode(fsp->conn,fsp->fsp_name,&st);
00226 if ((lp_store_dos_attributes(SNUM(fsp->conn)) || MAP_ARCHIVE(fsp->conn)) && !IS_DOS_ARCHIVE(dosmode)) {
00227 file_set_dosmode(fsp->conn,fsp->fsp_name,dosmode | aARCH,&st, False);
00228 }
00229
00230
00231
00232
00233
00234
00235 if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type) && !wcp) {
00236 setup_write_cache(fsp, st.st_size);
00237 wcp = fsp->wcp;
00238 }
00239 }
00240 }
00241
00242 #ifdef WITH_PROFILE
00243 DO_PROFILE_INC(writecache_total_writes);
00244 if (!fsp->oplock_type) {
00245 DO_PROFILE_INC(writecache_non_oplock_writes);
00246 }
00247 #endif
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257 release_level_2_oplocks_on_change(fsp);
00258
00259 #ifdef WITH_PROFILE
00260 if (profile_p && profile_p->writecache_total_writes % 500 == 0) {
00261 DEBUG(3,("WRITECACHE: initwrites=%u abutted=%u total=%u \
00262 nonop=%u allocated=%u active=%u direct=%u perfect=%u readhits=%u\n",
00263 profile_p->writecache_init_writes,
00264 profile_p->writecache_abutted_writes,
00265 profile_p->writecache_total_writes,
00266 profile_p->writecache_non_oplock_writes,
00267 profile_p->writecache_allocated_write_caches,
00268 profile_p->writecache_num_write_caches,
00269 profile_p->writecache_direct_writes,
00270 profile_p->writecache_num_perfect_writes,
00271 profile_p->writecache_read_hits ));
00272
00273 DEBUG(3,("WRITECACHE: Flushes SEEK=%d, READ=%d, WRITE=%d, READRAW=%d, OPLOCK=%d, CLOSE=%d, SYNC=%d\n",
00274 profile_p->writecache_flushed_writes[SEEK_FLUSH],
00275 profile_p->writecache_flushed_writes[READ_FLUSH],
00276 profile_p->writecache_flushed_writes[WRITE_FLUSH],
00277 profile_p->writecache_flushed_writes[READRAW_FLUSH],
00278 profile_p->writecache_flushed_writes[OPLOCK_RELEASE_FLUSH],
00279 profile_p->writecache_flushed_writes[CLOSE_FLUSH],
00280 profile_p->writecache_flushed_writes[SYNC_FLUSH] ));
00281 }
00282 #endif
00283
00284 if(!wcp) {
00285 DO_PROFILE_INC(writecache_direct_writes);
00286 total_written = real_write_file(fsp, data, pos, n);
00287 return total_written;
00288 }
00289
00290 DEBUG(9,("write_file (%s)(fd=%d pos=%.0f size=%u) wcp->offset=%.0f wcp->data_size=%u\n",
00291 fsp->fsp_name, fsp->fh->fd, (double)pos, (unsigned int)n, (double)wcp->offset, (unsigned int)wcp->data_size));
00292
00293 fsp->fh->pos = pos + n;
00294
00295
00296
00297
00298
00299
00300 if (wcp->data_size) {
00301 BOOL cache_flush_needed = False;
00302
00303 if ((pos >= wcp->offset) && (pos <= wcp->offset + wcp->data_size)) {
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321 size_t data_used = MIN((wcp->alloc_size - (pos - wcp->offset)), n);
00322
00323 memcpy(wcp->data + (pos - wcp->offset), data, data_used);
00324
00325
00326
00327
00328
00329 if(pos + data_used > wcp->offset + wcp->data_size) {
00330 wcp->data_size = pos + data_used - wcp->offset;
00331 }
00332
00333
00334
00335
00336
00337 if (wcp->offset + wcp->data_size > wcp->file_size) {
00338 if (wcp_file_size_change(fsp) == -1) {
00339 return -1;
00340 }
00341 }
00342
00343
00344
00345
00346
00347
00348 if(n == data_used) {
00349 return n;
00350 } else {
00351 cache_flush_needed = True;
00352 }
00353
00354
00355
00356
00357
00358 data += data_used;
00359 pos += data_used;
00360 n -= data_used;
00361
00362 DO_PROFILE_INC(writecache_abutted_writes);
00363 total_written = data_used;
00364
00365 write_path = 1;
00366
00367 } else if ((pos < wcp->offset) && (pos + n > wcp->offset) &&
00368 (pos + n <= wcp->offset + wcp->alloc_size)) {
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386 size_t data_used = pos + n - wcp->offset;
00387
00388 memcpy(wcp->data, data + n - data_used, data_used);
00389
00390
00391
00392
00393
00394 if(pos + n > wcp->offset + wcp->data_size) {
00395 wcp->data_size = pos + n - wcp->offset;
00396 }
00397
00398
00399
00400
00401
00402 if (wcp->offset + wcp->data_size > wcp->file_size) {
00403 if (wcp_file_size_change(fsp) == -1) {
00404 return -1;
00405 }
00406 }
00407
00408
00409
00410
00411
00412
00413 n -= data_used;
00414
00415
00416
00417
00418
00419 cache_flush_needed = True;
00420
00421 DO_PROFILE_INC(writecache_abutted_writes);
00422 total_written = data_used;
00423
00424 write_path = 2;
00425
00426 } else if ( (pos >= wcp->file_size) &&
00427 (wcp->offset + wcp->data_size == wcp->file_size) &&
00428 (pos > wcp->offset + wcp->data_size) &&
00429 (pos < wcp->offset + wcp->alloc_size) ) {
00430
00431
00432
00433
00434
00435
00436
00437
00438
00439
00440
00441
00442
00443
00444
00445
00446
00447
00448
00449
00450
00451
00452 size_t data_used;
00453
00454 if(pos + n <= wcp->offset + wcp->alloc_size) {
00455 data_used = n;
00456 } else {
00457 data_used = wcp->offset + wcp->alloc_size - pos;
00458 }
00459
00460
00461
00462
00463
00464 memset(wcp->data + wcp->data_size, '\0',
00465 pos - (wcp->offset + wcp->data_size) );
00466
00467 memcpy(wcp->data + (pos - wcp->offset), data, data_used);
00468
00469
00470
00471
00472
00473 if(pos + data_used > wcp->offset + wcp->data_size) {
00474 wcp->data_size = pos + data_used - wcp->offset;
00475 }
00476
00477
00478
00479
00480
00481 if (wcp->offset + wcp->data_size > wcp->file_size) {
00482 if (wcp_file_size_change(fsp) == -1) {
00483 return -1;
00484 }
00485 }
00486
00487
00488
00489
00490
00491
00492 if(n == data_used) {
00493 return n;
00494 } else {
00495 cache_flush_needed = True;
00496 }
00497
00498
00499
00500
00501
00502
00503 data += data_used;
00504 pos += data_used;
00505 n -= data_used;
00506
00507 DO_PROFILE_INC(writecache_abutted_writes);
00508 total_written = data_used;
00509
00510 write_path = 3;
00511
00512 } else if ( (pos >= wcp->file_size) &&
00513 (n == 1) &&
00514 (wcp->file_size == wcp->offset + wcp->data_size) &&
00515 (pos < wcp->file_size + wcp->alloc_size)) {
00516
00517
00518
00519
00520
00521
00522
00523
00524
00525
00526
00527
00528
00529
00530
00531
00532
00533
00534
00535
00536
00537
00538
00539
00540
00541
00542 flush_write_cache(fsp, WRITE_FLUSH);
00543 wcp->offset = wcp->file_size;
00544 wcp->data_size = pos - wcp->file_size + 1;
00545 memset(wcp->data, '\0', wcp->data_size);
00546 memcpy(wcp->data + wcp->data_size-1, data, 1);
00547
00548
00549
00550
00551
00552 if (wcp->offset + wcp->data_size > wcp->file_size) {
00553 if (wcp_file_size_change(fsp) == -1) {
00554 return -1;
00555 }
00556 }
00557
00558 return n;
00559
00560 } else {
00561
00562
00563
00564
00565
00566
00567
00568
00569
00570
00571
00572
00573
00574
00575
00576
00577
00578
00579
00580
00581
00582
00583
00584
00585
00586
00587
00588
00589
00590
00591
00592
00593
00594
00595
00596
00597
00598
00599
00600
00601 DEBUG(9,("write_file: non cacheable write : fd = %d, pos = %.0f, len = %u, current cache pos = %.0f \
00602 len = %u\n",fsp->fh->fd, (double)pos, (unsigned int)n, (double)wcp->offset, (unsigned int)wcp->data_size ));
00603
00604
00605
00606
00607
00608
00609
00610
00611 if ( n <= wcp->alloc_size && n > wcp->data_size) {
00612 cache_flush_needed = True;
00613 } else {
00614 ssize_t ret = real_write_file(fsp, data, pos, n);
00615
00616
00617
00618
00619
00620
00621
00622 if ((pos <= wcp->offset) &&
00623 (pos + n >= wcp->offset + wcp->data_size) ) {
00624 DEBUG(9,("write_file: discarding overwritten write \
00625 cache: fd = %d, off=%.0f, size=%u\n", fsp->fh->fd, (double)wcp->offset, (unsigned int)wcp->data_size ));
00626 wcp->data_size = 0;
00627 }
00628
00629 DO_PROFILE_INC(writecache_direct_writes);
00630 if (ret == -1) {
00631 return ret;
00632 }
00633
00634 if (pos + ret > wcp->file_size) {
00635 wcp->file_size = pos + ret;
00636 }
00637
00638 return ret;
00639 }
00640
00641 write_path = 4;
00642
00643 }
00644
00645 if (cache_flush_needed) {
00646 DEBUG(3,("WRITE_FLUSH:%d: due to noncontinuous write: fd = %d, size = %.0f, pos = %.0f, \
00647 n = %u, wcp->offset=%.0f, wcp->data_size=%u\n",
00648 write_path, fsp->fh->fd, (double)wcp->file_size, (double)pos, (unsigned int)n,
00649 (double)wcp->offset, (unsigned int)wcp->data_size ));
00650
00651 flush_write_cache(fsp, WRITE_FLUSH);
00652 }
00653 }
00654
00655
00656
00657
00658
00659
00660 if (n > wcp->alloc_size ) {
00661 ssize_t ret = real_write_file(fsp, data, pos, n);
00662 if (ret == -1) {
00663 return -1;
00664 }
00665
00666 if (pos + ret > wcp->file_size) {
00667 wcp->file_size = pos + n;
00668 }
00669
00670 DO_PROFILE_INC(writecache_direct_writes);
00671 return total_written + n;
00672 }
00673
00674
00675
00676
00677
00678 if (n) {
00679 #ifdef WITH_PROFILE
00680 if (wcp->data_size) {
00681 DO_PROFILE_INC(writecache_abutted_writes);
00682 } else {
00683 DO_PROFILE_INC(writecache_init_writes);
00684 }
00685 #endif
00686 memcpy(wcp->data+wcp->data_size, data, n);
00687 if (wcp->data_size == 0) {
00688 wcp->offset = pos;
00689 DO_PROFILE_INC(writecache_num_write_caches);
00690 }
00691 wcp->data_size += n;
00692
00693
00694
00695
00696
00697 if (wcp->offset + wcp->data_size > wcp->file_size) {
00698 if (wcp_file_size_change(fsp) == -1) {
00699 return -1;
00700 }
00701 }
00702 DEBUG(9,("wcp->offset = %.0f wcp->data_size = %u cache return %u\n",
00703 (double)wcp->offset, (unsigned int)wcp->data_size, (unsigned int)n));
00704
00705 total_written += n;
00706 return total_written;
00707 }
00708
00709 return total_written;
00710 }
00711
00712
00713
00714
00715
00716 void delete_write_cache(files_struct *fsp)
00717 {
00718 write_cache *wcp;
00719
00720 if(!fsp) {
00721 return;
00722 }
00723
00724 if(!(wcp = fsp->wcp)) {
00725 return;
00726 }
00727
00728 DO_PROFILE_DEC(writecache_allocated_write_caches);
00729 allocated_write_caches--;
00730
00731 SMB_ASSERT(wcp->data_size == 0);
00732
00733 SAFE_FREE(wcp->data);
00734 SAFE_FREE(fsp->wcp);
00735
00736 DEBUG(10,("delete_write_cache: File %s deleted write cache\n", fsp->fsp_name ));
00737 }
00738
00739
00740
00741
00742
00743 static BOOL setup_write_cache(files_struct *fsp, SMB_OFF_T file_size)
00744 {
00745 ssize_t alloc_size = lp_write_cache_size(SNUM(fsp->conn));
00746 write_cache *wcp;
00747
00748 if (allocated_write_caches >= MAX_WRITE_CACHES) {
00749 return False;
00750 }
00751
00752 if(alloc_size == 0 || fsp->wcp) {
00753 return False;
00754 }
00755
00756 if((wcp = SMB_MALLOC_P(write_cache)) == NULL) {
00757 DEBUG(0,("setup_write_cache: malloc fail.\n"));
00758 return False;
00759 }
00760
00761 wcp->file_size = file_size;
00762 wcp->offset = 0;
00763 wcp->alloc_size = alloc_size;
00764 wcp->data_size = 0;
00765 if((wcp->data = (char *)SMB_MALLOC(wcp->alloc_size)) == NULL) {
00766 DEBUG(0,("setup_write_cache: malloc fail for buffer size %u.\n",
00767 (unsigned int)wcp->alloc_size ));
00768 SAFE_FREE(wcp);
00769 return False;
00770 }
00771
00772 memset(wcp->data, '\0', wcp->alloc_size );
00773
00774 fsp->wcp = wcp;
00775 DO_PROFILE_INC(writecache_allocated_write_caches);
00776 allocated_write_caches++;
00777
00778 DEBUG(10,("setup_write_cache: File %s allocated write cache size %lu\n",
00779 fsp->fsp_name, (unsigned long)wcp->alloc_size ));
00780
00781 return True;
00782 }
00783
00784
00785
00786
00787
00788 void set_filelen_write_cache(files_struct *fsp, SMB_OFF_T file_size)
00789 {
00790 if(fsp->wcp) {
00791
00792 if (fsp->wcp->data_size != 0) {
00793 pstring msg;
00794 slprintf(msg, sizeof(msg)-1, "set_filelen_write_cache: size change \
00795 on file %s with write cache size = %lu\n", fsp->fsp_name, (unsigned long)fsp->wcp->data_size );
00796 smb_panic(msg);
00797 }
00798 fsp->wcp->file_size = file_size;
00799 }
00800 }
00801
00802
00803
00804
00805
00806 ssize_t flush_write_cache(files_struct *fsp, enum flush_reason_enum reason)
00807 {
00808 write_cache *wcp = fsp->wcp;
00809 size_t data_size;
00810 ssize_t ret;
00811
00812 if(!wcp || !wcp->data_size) {
00813 return 0;
00814 }
00815
00816 data_size = wcp->data_size;
00817 wcp->data_size = 0;
00818
00819 DO_PROFILE_DEC_INC(writecache_num_write_caches,writecache_flushed_writes[reason]);
00820
00821 DEBUG(9,("flushing write cache: fd = %d, off=%.0f, size=%u\n",
00822 fsp->fh->fd, (double)wcp->offset, (unsigned int)data_size));
00823
00824 #ifdef WITH_PROFILE
00825 if(data_size == wcp->alloc_size) {
00826 DO_PROFILE_INC(writecache_num_perfect_writes);
00827 }
00828 #endif
00829
00830 ret = real_write_file(fsp, wcp->data, wcp->offset, data_size);
00831
00832
00833
00834
00835
00836 if ((ret != -1) && (wcp->offset + ret > wcp->file_size)) {
00837 wcp->file_size = wcp->offset + ret;
00838 }
00839
00840 return ret;
00841 }
00842
00843
00844
00845
00846
00847 NTSTATUS sync_file(connection_struct *conn, files_struct *fsp, BOOL write_through)
00848 {
00849 if (fsp->fh->fd == -1)
00850 return NT_STATUS_INVALID_HANDLE;
00851
00852 if (lp_strict_sync(SNUM(conn)) &&
00853 (lp_syncalways(SNUM(conn)) || write_through)) {
00854 int ret = flush_write_cache(fsp, SYNC_FLUSH);
00855 if (ret == -1) {
00856 return map_nt_error_from_unix(errno);
00857 }
00858 ret = SMB_VFS_FSYNC(fsp,fsp->fh->fd);
00859 if (ret == -1) {
00860 return map_nt_error_from_unix(errno);
00861 }
00862 }
00863 return NT_STATUS_OK;
00864 }
00865
00866
00867
00868
00869
00870 int fsp_stat(files_struct *fsp, SMB_STRUCT_STAT *pst)
00871 {
00872 if (fsp->fh->fd == -1) {
00873 return SMB_VFS_STAT(fsp->conn, fsp->fsp_name, pst);
00874 } else {
00875 return SMB_VFS_FSTAT(fsp,fsp->fh->fd, pst);
00876 }
00877 }