static BOOL setup_write_cache(files_struct *, SMB_OFF_T);
-/****************************************************************************
- Seek a file. Try to avoid the seek if possible.
-****************************************************************************/
-
-static SMB_OFF_T seek_file(files_struct *fsp,SMB_OFF_T pos)
-{
- SMB_OFF_T seek_ret;
-
- seek_ret = fsp->conn->vfs_ops.lseek(fsp,fsp->fd,pos,SEEK_SET);
-
- if(seek_ret == -1) {
- DEBUG(0,("seek_file: (%s) sys_lseek failed. Error was %s\n",
- fsp->fsp_name, strerror(errno) ));
- fsp->pos = -1;
- return -1;
- }
-
- fsp->pos = seek_ret;
-
- DEBUG(10,("seek_file (%s): requested pos = %.0f, new pos = %.0f\n",
- fsp->fsp_name, (double)pos, (double)fsp->pos ));
-
- return(fsp->pos);
-}
-
/****************************************************************************
Read from write cache if we can.
****************************************************************************/
-
static BOOL read_from_write_cache(files_struct *fsp,char *data,SMB_OFF_T pos,size_t n)
{
write_cache *wcp = fsp->wcp;
- if(!wcp)
+ if(!wcp) {
return False;
+ }
- if(n > wcp->data_size || pos < wcp->offset || pos + n > wcp->offset + wcp->data_size)
+ if( n > wcp->data_size || pos < wcp->offset || pos + n > wcp->offset + wcp->data_size) {
return False;
+ }
memcpy(data, wcp->data + (pos - wcp->offset), n);
ssize_t ret=0,readret;
/* you can't read from print files */
- if (fsp->print_file)
+ if (fsp->print_file) {
return -1;
+ }
/*
* Serve from write cache if we can.
*/
- if(read_from_write_cache(fsp, data, pos, n))
+ if(read_from_write_cache(fsp, data, pos, n)) {
+ fsp->fh->pos = pos + n;
+ fsp->fh->position_information = fsp->fh->pos;
return n;
+ }
flush_write_cache(fsp, READ_FLUSH);
- if (seek_file(fsp,pos) == -1) {
- DEBUG(3,("read_file: Failed to seek to %.0f\n",(double)pos));
- return(ret);
- }
-
+ fsp->fh->pos = pos;
+
if (n > 0) {
#ifdef DMF_FIX
int numretries = 3;
tryagain:
- readret = fsp->conn->vfs_ops.read(fsp,fsp->fd,data,n);
+ readret = SMB_VFS_PREAD(fsp,fsp->fh->fd,data,n,pos);
+
if (readret == -1) {
if ((errno == EAGAIN) && numretries) {
DEBUG(3,("read_file EAGAIN retry in 10 seconds\n"));
return -1;
}
#else /* NO DMF fix. */
- readret = fsp->conn->vfs_ops.read(fsp,fsp->fd,data,n);
- if (readret == -1)
+ readret = SMB_VFS_PREAD(fsp,fsp->fh->fd,data,n,pos);
+
+ if (readret == -1) {
return -1;
+ }
#endif
- if (readret > 0)
+ if (readret > 0) {
ret += readret;
+ }
}
DEBUG(10,("read_file (%s): pos = %.0f, size = %lu, returned %lu\n",
fsp->fsp_name, (double)pos, (unsigned long)n, (long)ret ));
+ fsp->fh->pos += ret;
+ fsp->fh->position_information = fsp->fh->pos;
+
return(ret);
}
*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) && (seek_file(fsp,pos) == -1))
- return -1;
-
- ret = vfs_write_data(fsp,data,n);
+ if (pos == -1) {
+ ret = vfs_write_data(fsp, data, n);
+ } else {
+ 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);
+ }
DEBUG(10,("real_write_file (%s): pos = %.0f, size = %lu, returned %ld\n",
fsp->fsp_name, (double)pos, (unsigned long)n, (long)ret ));
+ if (ret != -1) {
+ fsp->fh->pos += ret;
+
+ /*
+ * It turns out that setting the last write time from a Windows
+ * client stops any subsequent writes from updating the write time.
+ * Doing this after the write gives a race condition here where
+ * a stat may see the changed write time before we reset it here,
+ * but it's cheaper than having to store the write time in shared
+ * memory and look it up using dev/inode across all running smbd's.
+ * The 99% solution will hopefully be good enough in this case. JRA.
+ */
+
+ if (fsp->pending_modtime) {
+ set_filetime(fsp->conn, fsp->fsp_name, fsp->pending_modtime);
+
+ /* If we didn't get the "set modtime" call ourselves, we must
+ store the last write time to restore on close. JRA. */
+ if (!fsp->pending_modtime_owner) {
+ fsp->last_write_time = time(NULL);
+ }
+ }
+
+/* Yes - this is correct - writes don't update this. JRA. */
+/* Found by Samba4 tests. */
+#if 0
+ fsp->position_information = fsp->pos;
+#endif
+ }
+
return ret;
}
/****************************************************************************
-write to a file
+ File size cache change.
+ Updates size on disk but doesn't flush the cache.
****************************************************************************/
-ssize_t write_file(files_struct *fsp, char *data, SMB_OFF_T pos, size_t n)
+static int wcp_file_size_change(files_struct *fsp)
+{
+ int ret;
+ write_cache *wcp = fsp->wcp;
+
+ wcp->file_size = wcp->offset + wcp->data_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) ));
+ }
+ return ret;
+}
+
+/****************************************************************************
+ Write to a file.
+****************************************************************************/
+
+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;
int write_path = -1;
- if (fsp->print_file)
- return print_job_write(SNUM(fsp->conn), fsp->print_jobid, data, n);
+ if (fsp->print_file) {
+ fstring sharename;
+ uint32 jobid;
+
+ if (!rap_to_pjobid(fsp->rap_print_jobid, sharename, &jobid)) {
+ DEBUG(3,("write_file: Unable to map RAP jobid %u to jobid.\n",
+ (unsigned int)fsp->rap_print_jobid ));
+ errno = EBADF;
+ return -1;
+ }
+
+ 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 (fsp->conn->vfs_ops.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);
- fsp->size = st.st_size;
- if (MAP_ARCHIVE(fsp->conn) && !IS_DOS_ARCHIVE(dosmode))
- file_chmod(fsp->conn,fsp->fsp_name,dosmode | aARCH,&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);
+ }
/*
* If this is the first write and we have an exclusive oplock then setup
if(!wcp) {
DO_PROFILE_INC(writecache_direct_writes);
total_written = real_write_file(fsp, data, pos, n);
- if ((total_written != -1) && (pos + total_written > fsp->size))
- fsp->size = pos + total_written;
return total_written;
}
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->fh->pos = pos + n;
/*
* If we have active cache and it isn't contiguous then we flush.
*/
if (wcp->data_size) {
-
BOOL cache_flush_needed = False;
if ((pos >= wcp->offset) && (pos <= wcp->offset + wcp->data_size)) {
* Update the current buffer size with the new data.
*/
- if(pos + data_used > wcp->offset + wcp->data_size)
+ if(pos + data_used > wcp->offset + wcp->data_size) {
wcp->data_size = pos + data_used - wcp->offset;
+ }
/*
* Update the file size if changed.
*/
- if (wcp->offset + wcp->data_size > wcp->file_size)
- fsp->size = wcp->file_size = wcp->offset + wcp->data_size;
+ if (wcp->offset + wcp->data_size > wcp->file_size) {
+ if (wcp_file_size_change(fsp) == -1) {
+ return -1;
+ }
+ }
/*
* If we used all the data then
* return here.
*/
- if(n == data_used)
+ if(n == data_used) {
return n;
- else
+ } else {
cache_flush_needed = True;
-
+ }
/*
* Move the start of data forward by the amount used,
* cut down the amount left by the same amount.
* Update the current buffer size with the new data.
*/
- if(pos + n > wcp->offset + wcp->data_size)
+ if(pos + n > wcp->offset + wcp->data_size) {
wcp->data_size = pos + n - wcp->offset;
+ }
/*
* Update the file size if changed.
*/
- if (wcp->offset + wcp->data_size > wcp->file_size)
- fsp->size = wcp->file_size = wcp->offset + wcp->data_size;
+ if (wcp->offset + wcp->data_size > wcp->file_size) {
+ if (wcp_file_size_change(fsp) == -1) {
+ return -1;
+ }
+ }
/*
* We don't need to move the start of data, but we
size_t data_used;
- if(pos + n <= wcp->offset + wcp->alloc_size)
+ if(pos + n <= wcp->offset + wcp->alloc_size) {
data_used = n;
- else
+ } else {
data_used = wcp->offset + wcp->alloc_size - pos;
+ }
/*
* Fill in the non-continuous area with zeros.
* Update the current buffer size with the new data.
*/
- if(pos + data_used > wcp->offset + wcp->data_size)
+ if(pos + data_used > wcp->offset + wcp->data_size) {
wcp->data_size = pos + data_used - wcp->offset;
+ }
/*
* Update the file size if changed.
*/
- if (wcp->offset + wcp->data_size > wcp->file_size)
- fsp->size = wcp->file_size = wcp->offset + wcp->data_size;
+ if (wcp->offset + wcp->data_size > wcp->file_size) {
+ if (wcp_file_size_change(fsp) == -1) {
+ return -1;
+ }
+ }
/*
* If we used all the data then
* return here.
*/
- if(n == data_used)
+ if(n == data_used) {
return n;
- else
+ } else {
cache_flush_needed = True;
+ }
/*
* Move the start of data forward by the amount used,
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.
*/
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 ));
-
- /*
- * Update the file size if needed.
- */
-
- if(pos + n > wcp->file_size)
- fsp->size = wcp->file_size = pos + n;
+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
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;
}
DO_PROFILE_INC(writecache_direct_writes);
- if (ret == -1)
+ if (ret == -1) {
return ret;
+ }
- if (pos + ret > wcp->file_size)
- fsp->size = wcp->file_size = pos + ret;
+ if (pos + ret > wcp->file_size) {
+ wcp->file_size = pos + ret;
+ }
return ret;
}
}
- if(wcp->data_size > wcp->file_size)
- fsp->size = wcp->file_size = wcp->data_size;
-
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);
if (n > wcp->alloc_size ) {
ssize_t ret = real_write_file(fsp, data, pos, n);
- if (ret == -1)
+ if (ret == -1) {
return -1;
+ }
- if (pos + ret > wcp->file_size)
- fsp->size = wcp->file_size = pos + n;
+ if (pos + ret > wcp->file_size) {
+ wcp->file_size = pos + n;
+ }
DO_PROFILE_INC(writecache_direct_writes);
return total_written + n;
* Update the file size if changed.
*/
- if (wcp->offset + wcp->data_size > wcp->file_size)
- fsp->size = wcp->file_size = wcp->offset + wcp->data_size;
+ if (wcp->offset + wcp->data_size > wcp->file_size) {
+ if (wcp_file_size_change(fsp) == -1) {
+ return -1;
+ }
+ }
DEBUG(9,("wcp->offset = %.0f wcp->data_size = %u cache return %u\n",
(double)wcp->offset, (unsigned int)wcp->data_size, (unsigned int)n));
{
write_cache *wcp;
- if(!fsp)
+ if(!fsp) {
return;
+ }
- if(!(wcp = fsp->wcp))
+ if(!(wcp = fsp->wcp)) {
return;
+ }
DO_PROFILE_DEC(writecache_allocated_write_caches);
allocated_write_caches--;
ssize_t alloc_size = lp_write_cache_size(SNUM(fsp->conn));
write_cache *wcp;
- if (allocated_write_caches >= MAX_WRITE_CACHES)
+ if (allocated_write_caches >= MAX_WRITE_CACHES) {
return False;
+ }
- if(alloc_size == 0 || fsp->wcp)
+ if(alloc_size == 0 || fsp->wcp) {
return False;
+ }
- if((wcp = (write_cache *)malloc(sizeof(write_cache))) == NULL) {
+ if((wcp = SMB_MALLOC_P(write_cache)) == NULL) {
DEBUG(0,("setup_write_cache: malloc fail.\n"));
return False;
}
wcp->offset = 0;
wcp->alloc_size = alloc_size;
wcp->data_size = 0;
- if((wcp->data = 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);
DO_PROFILE_INC(writecache_allocated_write_caches);
allocated_write_caches++;
- DEBUG(10,("setup_write_cache: File %s allocated write cache size %u\n",
- fsp->fsp_name, wcp->alloc_size ));
+ DEBUG(10,("setup_write_cache: File %s allocated write cache size %lu\n",
+ fsp->fsp_name, (unsigned long)wcp->alloc_size ));
return True;
}
void set_filelen_write_cache(files_struct *fsp, SMB_OFF_T file_size)
{
- fsp->size = file_size;
if(fsp->wcp) {
/* The cache *must* have been flushed before we do this. */
if (fsp->wcp->data_size != 0) {
pstring msg;
slprintf(msg, sizeof(msg)-1, "set_filelen_write_cache: size change \
-on file %s with write cache size = %u\n", fsp->fsp_name, fsp->wcp->data_size );
+on file %s with write cache size = %lu\n", fsp->fsp_name, (unsigned long)fsp->wcp->data_size );
smb_panic(msg);
}
fsp->wcp->file_size = file_size;
size_t data_size;
ssize_t ret;
- if(!wcp || !wcp->data_size)
+ if(!wcp || !wcp->data_size) {
return 0;
+ }
data_size = wcp->data_size;
wcp->data_size = 0;
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)
+ if(data_size == wcp->alloc_size) {
DO_PROFILE_INC(writecache_num_perfect_writes);
+ }
#endif
ret = real_write_file(fsp, wcp->data, wcp->offset, data_size);
* Ensure file size if kept up to date if write extends file.
*/
- if ((ret != -1) && (wcp->offset + ret > wcp->file_size))
+ if ((ret != -1) && (wcp->offset + ret > wcp->file_size)) {
wcp->file_size = wcp->offset + ret;
+ }
return ret;
}
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);
- conn->vfs_ops.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)
- return vfs_stat(fsp->conn, fsp->fsp_name, pst);
- else
- return vfs_fstat(fsp,fsp->fd, pst);
+ if (fsp->fh->fd == -1) {
+ return SMB_VFS_STAT(fsp->conn, fsp->fsp_name, pst);
+ } else {
+ return SMB_VFS_FSTAT(fsp,fsp->fh->fd, pst);
+ }
}