r20844: Somewhat radical change - this may break the build (I will
[samba.git] / source3 / smbd / fileio.c
index dbf1e5a789d0026c473b9c79fb49d12c15d97bca..e0945be8893e220031b13ce91899929ecf478aff 100644 (file)
@@ -65,20 +65,20 @@ ssize_t read_file(files_struct *fsp,char *data,SMB_OFF_T pos,size_t n)
         */
 
        if(read_from_write_cache(fsp, data, pos, n)) {
-               fsp->pos = pos + n;
-               fsp->position_information = fsp->pos;
+               fsp->fh->pos = pos + n;
+               fsp->fh->position_information = fsp->fh->pos;
                return n;
        }
 
        flush_write_cache(fsp, READ_FLUSH);
 
-       fsp->pos = pos;
+       fsp->fh->pos = pos;
 
        if (n > 0) {
 #ifdef DMF_FIX
                int numretries = 3;
 tryagain:
-               readret = SMB_VFS_PREAD(fsp,fsp->fd,data,n,pos);
+               readret = SMB_VFS_PREAD(fsp,fsp->fh->fd,data,n,pos);
 
                if (readret == -1) {
                        if ((errno == EAGAIN) && numretries) {
@@ -90,7 +90,7 @@ tryagain:
                        return -1;
                }
 #else /* NO DMF fix. */
-               readret = SMB_VFS_PREAD(fsp,fsp->fd,data,n,pos);
+               readret = SMB_VFS_PREAD(fsp,fsp->fh->fd,data,n,pos);
 
                if (readret == -1) {
                        return -1;
@@ -104,8 +104,8 @@ tryagain:
        DEBUG(10,("read_file (%s): pos = %.0f, size = %lu, returned %lu\n",
                fsp->fsp_name, (double)pos, (unsigned long)n, (long)ret ));
 
-       fsp->pos += ret;
-       fsp->position_information = fsp->pos;
+       fsp->fh->pos += ret;
+       fsp->fh->position_information = fsp->fh->pos;
 
        return(ret);
 }
@@ -117,14 +117,19 @@ static unsigned int allocated_write_caches;
  *Really* write to a file.
 ****************************************************************************/
 
-static ssize_t real_write_file(files_struct *fsp,char *data,SMB_OFF_T pos, size_t n)
+static ssize_t real_write_file(files_struct *fsp,const char *data, SMB_OFF_T pos, size_t n)
 {
        ssize_t ret;
 
         if (pos == -1) {
                 ret = vfs_write_data(fsp, data, n);
         } else {
-               fsp->pos = pos;
+               fsp->fh->pos = pos;
+               if (pos && lp_strict_allocate(SNUM(fsp->conn))) {
+                       if (vfs_fill_sparse(fsp, pos) == -1) {
+                               return -1;
+                       }
+               }
                 ret = vfs_pwrite_data(fsp, data, n, pos);
        }
 
@@ -132,7 +137,7 @@ static ssize_t real_write_file(files_struct *fsp,char *data,SMB_OFF_T pos, size_
                fsp->fsp_name, (double)pos, (unsigned long)n, (long)ret ));
 
        if (ret != -1) {
-               fsp->pos += ret;
+               fsp->fh->pos += ret;
 
                /*
                 * It turns out that setting the last write time from a Windows
@@ -175,7 +180,7 @@ static int wcp_file_size_change(files_struct *fsp)
        write_cache *wcp = fsp->wcp;
 
        wcp->file_size = wcp->offset + wcp->data_size;
-       ret = SMB_VFS_FTRUNCATE(fsp, fsp->fd, wcp->file_size);
+       ret = SMB_VFS_FTRUNCATE(fsp, fsp->fh->fd, wcp->file_size);
        if (ret == -1) {
                DEBUG(0,("wcp_file_size_change (%s): ftruncate of size %.0f error %s\n",
                        fsp->fsp_name, (double)wcp->file_size, strerror(errno) ));
@@ -187,7 +192,7 @@ static int wcp_file_size_change(files_struct *fsp)
  Write to a file.
 ****************************************************************************/
 
-ssize_t write_file(files_struct *fsp, char *data, SMB_OFF_T pos, size_t n)
+ssize_t write_file(files_struct *fsp, const char *data, SMB_OFF_T pos, size_t n)
 {
        write_cache *wcp = fsp->wcp;
        ssize_t total_written = 0;
@@ -204,19 +209,19 @@ ssize_t write_file(files_struct *fsp, char *data, SMB_OFF_T pos, size_t n)
                        return -1;
                }
 
-               return print_job_write(SNUM(fsp->conn), jobid, data, n);
+               return print_job_write(SNUM(fsp->conn), jobid, data, pos, n);
        }
 
        if (!fsp->can_write) {
                errno = EPERM;
-               return(0);
+               return -1;
        }
 
        if (!fsp->modified) {
                SMB_STRUCT_STAT st;
                fsp->modified = True;
 
-               if (SMB_VFS_FSTAT(fsp,fsp->fd,&st) == 0) {
+               if (SMB_VFS_FSTAT(fsp,fsp->fh->fd,&st) == 0) {
                        int dosmode = dos_mode(fsp->conn,fsp->fsp_name,&st);
                        if ((lp_store_dos_attributes(SNUM(fsp->conn)) || MAP_ARCHIVE(fsp->conn)) && !IS_DOS_ARCHIVE(dosmode)) {
                                file_set_dosmode(fsp->conn,fsp->fsp_name,dosmode | aARCH,&st, False);
@@ -283,9 +288,9 @@ nonop=%u allocated=%u active=%u direct=%u perfect=%u readhits=%u\n",
        }
 
        DEBUG(9,("write_file (%s)(fd=%d pos=%.0f size=%u) wcp->offset=%.0f wcp->data_size=%u\n",
-               fsp->fsp_name, fsp->fd, (double)pos, (unsigned int)n, (double)wcp->offset, (unsigned int)wcp->data_size));
+               fsp->fsp_name, fsp->fh->fd, (double)pos, (unsigned int)n, (double)wcp->offset, (unsigned int)wcp->data_size));
 
-       fsp->pos = pos + n;
+       fsp->fh->pos = pos + n;
 
        /* 
         * If we have active cache and it isn't contiguous then we flush.
@@ -504,6 +509,44 @@ nonop=%u allocated=%u active=%u direct=%u perfect=%u readhits=%u\n",
 
                        write_path = 3;
 
+                } else if ( (pos >= wcp->file_size) && 
+                           (n == 1) &&
+                           (pos < wcp->offset + 2*wcp->alloc_size) &&
+                           (wcp->file_size == wcp->offset + wcp->data_size)) {
+
+                        /*
+                        +---------------+
+                        | Cached data   |
+                        +---------------+
+
+                                                         +--------+
+                                                         | 1 Byte |
+                                                         +--------+
+
+                       MS-Office seems to do this a lot to determine if there's enough
+                       space on the filesystem to write a new file.
+                        */
+
+                       SMB_BIG_UINT new_start = wcp->offset + wcp->data_size;
+
+                       flush_write_cache(fsp, WRITE_FLUSH);
+                       wcp->offset = new_start;
+                       wcp->data_size = pos - new_start + 1;
+                       memset(wcp->data, '\0', wcp->data_size);
+                       memcpy(wcp->data + wcp->data_size-1, data, 1);
+
+                       /*
+                        * Update the file size if changed.
+                        */
+
+                       if (wcp->offset + wcp->data_size > wcp->file_size) {
+                               if (wcp_file_size_change(fsp) == -1) {
+                                       return -1;
+                               }
+                       }
+
+                       return n;
+
                } else {
 
                        /* ASCII art..... JRA.
@@ -546,7 +589,7 @@ nonop=%u allocated=%u active=%u direct=%u perfect=%u readhits=%u\n",
                         */
 
                        DEBUG(9,("write_file: non cacheable write : fd = %d, pos = %.0f, len = %u, current cache pos = %.0f \
-len = %u\n",fsp->fd, (double)pos, (unsigned int)n, (double)wcp->offset, (unsigned int)wcp->data_size ));
+len = %u\n",fsp->fh->fd, (double)pos, (unsigned int)n, (double)wcp->offset, (unsigned int)wcp->data_size ));
 
                        /*
                         * If write would fit in the cache, and is larger than
@@ -569,7 +612,7 @@ len = %u\n",fsp->fd, (double)pos, (unsigned int)n, (double)wcp->offset, (unsigne
                                if ((pos <= wcp->offset) &&
                                                (pos + n >= wcp->offset + wcp->data_size) ) {
                                        DEBUG(9,("write_file: discarding overwritten write \
-cache: fd = %d, off=%.0f, size=%u\n", fsp->fd, (double)wcp->offset, (unsigned int)wcp->data_size ));
+cache: fd = %d, off=%.0f, size=%u\n", fsp->fh->fd, (double)wcp->offset, (unsigned int)wcp->data_size ));
                                        wcp->data_size = 0;
                                }
 
@@ -592,7 +635,7 @@ cache: fd = %d, off=%.0f, size=%u\n", fsp->fd, (double)wcp->offset, (unsigned in
                if (cache_flush_needed) {
                        DEBUG(3,("WRITE_FLUSH:%d: due to noncontinuous write: fd = %d, size = %.0f, pos = %.0f, \
 n = %u, wcp->offset=%.0f, wcp->data_size=%u\n",
-                               write_path, fsp->fd, (double)wcp->file_size, (double)pos, (unsigned int)n,
+                               write_path, fsp->fh->fd, (double)wcp->file_size, (double)pos, (unsigned int)n,
                                (double)wcp->offset, (unsigned int)wcp->data_size ));
 
                        flush_write_cache(fsp, WRITE_FLUSH);
@@ -709,7 +752,7 @@ static BOOL setup_write_cache(files_struct *fsp, SMB_OFF_T file_size)
        wcp->offset = 0;
        wcp->alloc_size = alloc_size;
        wcp->data_size = 0;
-       if((wcp->data = SMB_MALLOC(wcp->alloc_size)) == NULL) {
+       if((wcp->data = (char *)SMB_MALLOC(wcp->alloc_size)) == NULL) {
                DEBUG(0,("setup_write_cache: malloc fail for buffer size %u.\n",
                        (unsigned int)wcp->alloc_size ));
                SAFE_FREE(wcp);
@@ -766,7 +809,7 @@ ssize_t flush_write_cache(files_struct *fsp, enum flush_reason_enum reason)
        DO_PROFILE_DEC_INC(writecache_num_write_caches,writecache_flushed_writes[reason]);
 
        DEBUG(9,("flushing write cache: fd = %d, off=%.0f, size=%u\n",
-               fsp->fd, (double)wcp->offset, (unsigned int)data_size));
+               fsp->fh->fd, (double)wcp->offset, (unsigned int)data_size));
 
 #ifdef WITH_PROFILE
        if(data_size == wcp->alloc_size) {
@@ -791,24 +834,27 @@ ssize_t flush_write_cache(files_struct *fsp, enum flush_reason_enum reason)
 sync a file
 ********************************************************************/
 
-void sync_file(connection_struct *conn, files_struct *fsp)
+void sync_file(connection_struct *conn, files_struct *fsp, BOOL write_through)
 {
-       if(lp_strict_sync(SNUM(conn)) && fsp->fd != -1) {
+               if (fsp->fh->fd == -1)
+               return;
+
+       if (lp_strict_sync(SNUM(conn)) &&
+           (lp_syncalways(SNUM(conn)) || write_through)) {
                flush_write_cache(fsp, SYNC_FLUSH);
-               SMB_VFS_FSYNC(fsp,fsp->fd);
+               SMB_VFS_FSYNC(fsp,fsp->fh->fd);
        }
 }
 
-
 /************************************************************
  Perform a stat whether a valid fd or not.
 ************************************************************/
 
 int fsp_stat(files_struct *fsp, SMB_STRUCT_STAT *pst)
 {
-       if (fsp->fd == -1) {
+       if (fsp->fh->fd == -1) {
                return SMB_VFS_STAT(fsp->conn, fsp->fsp_name, pst);
        } else {
-               return SMB_VFS_FSTAT(fsp,fsp->fd, pst);
+               return SMB_VFS_FSTAT(fsp,fsp->fh->fd, pst);
        }
 }