*/
#include "includes.h"
+#include "printing.h"
+#include "smbd/globals.h"
static bool setup_write_cache(files_struct *, SMB_OFF_T);
/* you can't read from print files */
if (fsp->print_file) {
+ errno = EBADF;
return -1;
}
}
DEBUG(10,("read_file (%s): pos = %.0f, size = %lu, returned %lu\n",
- fsp->fsp_name, (double)pos, (unsigned long)n, (long)ret ));
+ fsp_str_dbg(fsp), (double)pos, (unsigned long)n, (long)ret));
fsp->fh->pos += ret;
fsp->fh->position_information = fsp->fh->pos;
return(ret);
}
-/* how many write cache buffers have been allocated */
-static unsigned int allocated_write_caches;
-
/****************************************************************************
*Really* write to a file.
****************************************************************************/
ret = vfs_write_data(req, fsp, data, n);
} else {
fsp->fh->pos = pos;
- if (pos && lp_strict_allocate(SNUM(fsp->conn))) {
+ if (pos && lp_strict_allocate(SNUM(fsp->conn) &&
+ !fsp->is_sparse)) {
if (vfs_fill_sparse(fsp, pos) == -1) {
return -1;
}
}
DEBUG(10,("real_write_file (%s): pos = %.0f, size = %lu, returned %ld\n",
- fsp->fsp_name, (double)pos, (unsigned long)n, (long)ret ));
+ fsp_str_dbg(fsp), (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 (!null_timespec(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 = timespec_current();
- }
- }
-
/* Yes - this is correct - writes don't update this. JRA. */
/* Found by Samba4 tests. */
#if 0
wcp->file_size = wcp->offset + wcp->data_size;
ret = SMB_VFS_FTRUNCATE(fsp, 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) ));
+ DEBUG(0,("wcp_file_size_change (%s): ftruncate of size %.0f "
+ "error %s\n", fsp_str_dbg(fsp),
+ (double)wcp->file_size, strerror(errno)));
}
return ret;
}
+void update_write_time_handler(struct event_context *ctx,
+ struct timed_event *te,
+ struct timeval now,
+ void *private_data)
+{
+ files_struct *fsp = (files_struct *)private_data;
+
+ DEBUG(5, ("Update write time on %s\n", fsp_str_dbg(fsp)));
+
+ /* change the write time in the open file db. */
+ (void)set_write_time(fsp->file_id, timespec_current());
+
+ /* And notify. */
+ notify_fname(fsp->conn, NOTIFY_ACTION_MODIFIED,
+ FILE_NOTIFY_CHANGE_LAST_WRITE, fsp->fsp_name->base_name);
+
+ /* Remove the timed event handler. */
+ TALLOC_FREE(fsp->update_write_time_event);
+}
+
+/*********************************************************
+ Schedule a write time update for WRITE_TIME_UPDATE_USEC_DELAY
+ in the future.
+*********************************************************/
+
+void trigger_write_time_update(struct files_struct *fsp)
+{
+ int delay;
+
+ if (fsp->posix_open) {
+ /* Don't use delayed writes on POSIX files. */
+ return;
+ }
+
+ if (fsp->write_time_forced) {
+ /* No point - "sticky" write times
+ * in effect.
+ */
+ return;
+ }
+
+ /* We need to remember someone did a write
+ * and update to current time on close. */
+
+ fsp->update_write_time_on_close = true;
+
+ if (fsp->update_write_time_triggered) {
+ /*
+ * We only update the write time after 2 seconds
+ * on the first normal write. After that
+ * no other writes affect this until close.
+ */
+ return;
+ }
+ fsp->update_write_time_triggered = true;
+
+ delay = lp_parm_int(SNUM(fsp->conn),
+ "smbd", "writetimeupdatedelay",
+ WRITE_TIME_UPDATE_USEC_DELAY);
+
+ DEBUG(5, ("Update write time %d usec later on %s\n",
+ delay, fsp_str_dbg(fsp)));
+
+ /* trigger the update 2 seconds later */
+ fsp->update_write_time_event =
+ event_add_timed(smbd_event_context(), NULL,
+ timeval_current_ofs(0, delay),
+ update_write_time_handler, fsp);
+}
+
+void trigger_write_time_update_immediate(struct files_struct *fsp)
+{
+ struct smb_file_time ft;
+
+ if (fsp->posix_open) {
+ /* Don't use delayed writes on POSIX files. */
+ return;
+ }
+
+ if (fsp->write_time_forced) {
+ /*
+ * No point - "sticky" write times
+ * in effect.
+ */
+ return;
+ }
+
+ TALLOC_FREE(fsp->update_write_time_event);
+ DEBUG(5, ("Update write time immediate on %s\n",
+ fsp_str_dbg(fsp)));
+
+ /* After an immediate update, reset the trigger. */
+ fsp->update_write_time_triggered = true;
+ fsp->update_write_time_on_close = false;
+
+ ZERO_STRUCT(ft);
+ ft.mtime = timespec_current();
+
+ /* Update the time in the open file db. */
+ (void)set_write_time(fsp->file_id, ft.mtime);
+
+ /* Now set on disk - takes care of notify. */
+ (void)smb_set_file_time(fsp->conn, fsp, fsp->fsp_name, &ft, false);
+}
+
/****************************************************************************
Write to a file.
****************************************************************************/
int write_path = -1;
if (fsp->print_file) {
- fstring sharename;
- uint32 jobid;
+ uint32_t t;
+ int ret;
- 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;
+ ret = print_spool_write(fsp, data, n, pos, &t);
+ if (ret) {
+ errno = ret;
return -1;
}
-
- return print_job_write(SNUM(fsp->conn), jobid, data, pos, n);
+ return t;
}
if (!fsp->can_write) {
}
if (!fsp->modified) {
- SMB_STRUCT_STAT st;
fsp->modified = True;
- if (SMB_VFS_FSTAT(fsp, &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,
- NULL,
- false);
+ if (SMB_VFS_FSTAT(fsp, &fsp->fsp_name->st) == 0) {
+ trigger_write_time_update(fsp);
+ if (!fsp->posix_open &&
+ (lp_store_dos_attributes(SNUM(fsp->conn)) ||
+ MAP_ARCHIVE(fsp->conn))) {
+ int dosmode = dos_mode(fsp->conn, fsp->fsp_name);
+ if (!IS_DOS_ARCHIVE(dosmode)) {
+ file_set_dosmode(fsp->conn, fsp->fsp_name,
+ dosmode | aARCH, NULL, false);
+ }
}
/*
*/
if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type) && !wcp) {
- setup_write_cache(fsp, st.st_size);
+ setup_write_cache(fsp,
+ fsp->fsp_name->st.st_ex_size);
wcp = fsp->wcp;
}
}
* the shared memory area whilst doing this.
*/
- release_level_2_oplocks_on_change(fsp);
+ /* This should actually be improved to span the write. */
+ contend_level2_oplocks_begin(fsp, LEVEL2_CONTEND_WRITE);
+ contend_level2_oplocks_end(fsp, LEVEL2_CONTEND_WRITE);
#ifdef WITH_PROFILE
if (profile_p && profile_p->writecache_total_writes % 500 == 0) {
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->fh->fd, (double)pos, (unsigned int)n, (double)wcp->offset, (unsigned int)wcp->data_size));
+ DEBUG(9,("write_file (%s)(fd=%d pos=%.0f size=%u) wcp->offset=%.0f "
+ "wcp->data_size=%u\n", fsp_str_dbg(fsp), fsp->fh->fd,
+ (double)pos, (unsigned int)n, (double)wcp->offset,
+ (unsigned int)wcp->data_size));
fsp->fh->pos = pos + n;
+ if ((n == 1) && (data[0] == '\0') && (pos > wcp->file_size)) {
+ int ret;
+
+ /*
+ * This is a 1-byte write of a 0 beyond the EOF and
+ * thus implicitly also beyond the current active
+ * write cache, the typical file-extending (and
+ * allocating, but we're using the write cache here)
+ * write done by Windows. We just have to ftruncate
+ * the file and rely on posix semantics to return
+ * zeros for non-written file data that is within the
+ * file length.
+ *
+ * We can not use wcp_file_size_change here because we
+ * might have an existing write cache, and
+ * wcp_file_size_change assumes a change to just the
+ * end of the current write cache.
+ */
+
+ wcp->file_size = pos + 1;
+ ret = SMB_VFS_FTRUNCATE(fsp, wcp->file_size);
+ if (ret == -1) {
+ DEBUG(0,("wcp_file_size_change (%s): ftruncate of size %.0f"
+ "error %s\n", fsp_str_dbg(fsp),
+ (double)wcp->file_size, strerror(errno)));
+ return -1;
+ }
+ return 1;
+ }
+
+
/*
* If we have active cache and it isn't contiguous then we flush.
* NOTE: There is a small problem with running out of disk ....
DO_PROFILE_INC(writecache_init_writes);
}
#endif
+
+ if ((wcp->data_size == 0)
+ && (pos > wcp->file_size)
+ && (pos + n <= wcp->file_size + wcp->alloc_size)) {
+ /*
+ * This is a write completely beyond the
+ * current EOF, but within reach of the write
+ * cache. We expect fill-up writes pretty
+ * soon, so it does not make sense to start
+ * the write cache at the current
+ * offset. These fill-up writes would trigger
+ * separate pwrites or even unnecessary cache
+ * flushes because they overlap if this is a
+ * one-byte allocating write.
+ */
+ wcp->offset = wcp->file_size;
+ wcp->data_size = pos - wcp->file_size;
+ memset(wcp->data, 0, wcp->data_size);
+ }
+
memcpy(wcp->data+wcp->data_size, data, n);
if (wcp->data_size == 0) {
wcp->offset = pos;
SAFE_FREE(wcp->data);
SAFE_FREE(fsp->wcp);
- DEBUG(10,("delete_write_cache: File %s deleted write cache\n", fsp->fsp_name ));
+ DEBUG(10,("delete_write_cache: File %s deleted write cache\n",
+ fsp_str_dbg(fsp)));
}
/****************************************************************************
allocated_write_caches++;
DEBUG(10,("setup_write_cache: File %s allocated write cache size %lu\n",
- fsp->fsp_name, (unsigned long)wcp->alloc_size ));
+ fsp_str_dbg(fsp), (unsigned long)wcp->alloc_size));
return True;
}
/* The cache *must* have been flushed before we do this. */
if (fsp->wcp->data_size != 0) {
char *msg;
- asprintf(&msg, "set_filelen_write_cache: size change "
+ if (asprintf(&msg, "set_filelen_write_cache: size change "
"on file %s with write cache size = %lu\n",
- fsp->fsp_name,
- (unsigned long)fsp->wcp->data_size);
- smb_panic(msg);
+ fsp->fsp_name->base_name,
+ (unsigned long)fsp->wcp->data_size) != -1) {
+ smb_panic(msg);
+ } else {
+ smb_panic("set_filelen_write_cache");
+ }
}
fsp->wcp->file_size = file_size;
}
Perform a stat whether a valid fd or not.
************************************************************/
-int fsp_stat(files_struct *fsp, SMB_STRUCT_STAT *pst)
+int fsp_stat(files_struct *fsp)
{
if (fsp->fh->fd == -1) {
- return SMB_VFS_STAT(fsp->conn, fsp->fsp_name, pst);
+ if (fsp->posix_open) {
+ return SMB_VFS_LSTAT(fsp->conn, fsp->fsp_name);
+ } else {
+ return SMB_VFS_STAT(fsp->conn, fsp->fsp_name);
+ }
} else {
- return SMB_VFS_FSTAT(fsp, pst);
+ return SMB_VFS_FSTAT(fsp, &fsp->fsp_name->st);
}
}