summaryrefslogtreecommitdiff
path: root/source3/modules
diff options
context:
space:
mode:
Diffstat (limited to 'source3/modules')
-rw-r--r--source3/modules/vfs_commit.c183
1 files changed, 158 insertions, 25 deletions
diff --git a/source3/modules/vfs_commit.c b/source3/modules/vfs_commit.c
index 39de7f7e74..d7d81924f1 100644
--- a/source3/modules/vfs_commit.c
+++ b/source3/modules/vfs_commit.c
@@ -1,5 +1,6 @@
/*
- * Copyright (c) James Peach 2006
+ * Copyright (c) James Peach 2006, 2007
+ * Copyright (c) David Losada Carballo 2007
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -29,23 +30,73 @@
* Tunables:
*
* commit: dthresh Amount of dirty data that can accumulate
- * before we commit (sync) it.
+ * before we commit (sync) it.
*
* commit: debug Debug level at which to emit messages.
*
+ * commit: eof mode String. Tunes how the module tries to guess when
+ * the client has written the last bytes of the file.
+ * Possible values (default = hinted):
+ *
+ * (*) = hinted Some clients (i.e. Windows Explorer) declare the
+ * size of the file before transferring it. With this
+ * option, we remember that hint, and commit after
+ * writing in that file position. If the client
+ * doesn't declare the size of file, commiting on EOF
+ * is not triggered.
+ *
+ * = growth Commits after a write operation has made the file
+ * size grow. If the client declares a file size, it
+ * refrains to commit until the file has reached it.
+ * Useful for defeating writeback on NFS shares.
+ *
*/
#define MODULE "commit"
static int module_debug;
+enum eof_mode
+{
+ EOF_NONE = 0x0000,
+ EOF_HINTED = 0x0001,
+ EOF_GROWTH = 0x0002
+};
+
struct commit_info
{
+ /* For chunk-based commits */
SMB_OFF_T dbytes; /* Dirty (uncommitted) bytes */
SMB_OFF_T dthresh; /* Dirty data threshold */
+ /* For commits on EOF */
+ enum eof_mode on_eof;
+ SMB_OFF_T eof; /* Expected file size */
};
-static void commit_all(
+static int commit_do(
+ struct commit_info * c,
+ int fd)
+{
+ int result;
+
+ DEBUG(module_debug,
+ ("%s: flushing %lu dirty bytes\n",
+ MODULE, (unsigned long)c->dbytes));
+
+#if HAVE_FDATASYNC
+ result = fdatasync(fd);
+#elif HAVE_FSYNC
+ result = fsync(fd);
+#else
+ result = 0
+#endif
+ if (result == 0) {
+ c->dbytes = 0; /* on success, no dirty bytes */
+ }
+ return result;
+}
+
+static int commit_all(
struct vfs_handle_struct * handle,
files_struct * fsp)
{
@@ -57,34 +108,52 @@ static void commit_all(
("%s: flushing %lu dirty bytes\n",
MODULE, (unsigned long)c->dbytes));
- fdatasync(fsp->fh->fd);
- c->dbytes = 0;
+ return commit_do(c, fsp->fh->fd);
}
}
+ return 0;
}
-static void commit(
+static int commit(
struct vfs_handle_struct * handle,
files_struct * fsp,
+ SMB_OFF_T offset,
ssize_t last_write)
{
struct commit_info *c;
- if ((c = VFS_FETCH_FSP_EXTENSION(handle, fsp))) {
+ if ((c = VFS_FETCH_FSP_EXTENSION(handle, fsp)) == NULL) {
+ return 0;
+ }
- if (last_write > 0) {
- c->dbytes += last_write;
- }
+ c->dbytes += last_write; /* dirty bytes always counted */
- if (c->dbytes > c->dthresh) {
- DEBUG(module_debug,
- ("%s: flushing %lu dirty bytes\n",
- MODULE, (unsigned long)c->dbytes));
+ if (c->dthresh && (c->dbytes > c->dthresh)) {
+ return commit_do(c, fsp->fh->fd);
+ }
- fdatasync(fsp->fh->fd);
- c->dbytes = 0;
- }
- }
+ /* Return if we are not in EOF mode or if we have temporarily opted
+ * out of it.
+ */
+ if (c->on_eof == EOF_NONE || c->eof < 0) {
+ return 0;
+ }
+
+ /* This write hit or went past our cache the file size. */
+ if ((offset + last_write) >= c->eof) {
+ if (commit_do(c, fsp->fh->fd) == -1) {
+ return -1;
+ }
+
+ /* Hinted mode only commits the first time we hit EOF. */
+ if (c->on_eof == EOF_HINTED) {
+ c->eof = -1;
+ } else if (c->on_eof == EOF_GROWTH) {
+ c->eof = offset + last_write;
+ }
+ }
+
+ return 0;
}
static int commit_connect(
@@ -104,25 +173,57 @@ static int commit_open(
mode_t mode)
{
SMB_OFF_T dthresh;
+ const char *eof_mode;
+ struct commit_info *c = NULL;
+ int fd;
/* Don't bother with read-only files. */
if ((flags & O_ACCMODE) == O_RDONLY) {
return SMB_VFS_NEXT_OPEN(handle, fname, fsp, flags, mode);
}
+ /* Read and check module configuration */
dthresh = conv_str_size(lp_parm_const_string(SNUM(handle->conn),
MODULE, "dthresh", NULL));
- if (dthresh > 0) {
- struct commit_info * c;
+ eof_mode = lp_parm_const_string(SNUM(handle->conn),
+ MODULE, "eof mode", "none");
+
+ if (dthresh > 0 || !strequal(eof_mode, "none")) {
c = VFS_ADD_FSP_EXTENSION(handle, fsp, struct commit_info);
+ /* Process main tunables */
if (c) {
c->dthresh = dthresh;
c->dbytes = 0;
+ c->on_eof = EOF_NONE;
+ c->eof = 0;
+ }
+ }
+ /* Process eof_mode tunable */
+ if (c) {
+ if (strequal(eof_mode, "hinted")) {
+ c->on_eof = EOF_HINTED;
+ } else if (strequal(eof_mode, "growth")) {
+ c->on_eof = EOF_GROWTH;
+ }
+ }
+
+ fd = SMB_VFS_NEXT_OPEN(handle, fname, fsp, flags, mode);
+ if (fd == -1) {
+ VFS_REMOVE_FSP_EXTENSION(handle, fsp);
+ return fd;
+ }
+
+ /* EOF commit modes require us to know the initial file size. */
+ if (c && (c->on_eof != EOF_NONE)) {
+ SMB_STRUCT_STAT st;
+ if (SMB_VFS_FSTAT(fsp, fd, &st) == -1) {
+ return -1;
}
+ c->eof = st.st_size;
}
- return SMB_VFS_NEXT_OPEN(handle, fname, fsp, flags, mode);
+ return 0;
}
static ssize_t commit_write(
@@ -133,9 +234,13 @@ static ssize_t commit_write(
size_t count)
{
ssize_t ret;
-
ret = SMB_VFS_NEXT_WRITE(handle, fsp, fd, data, count);
- commit(handle, fsp, ret);
+
+ if (ret > 0) {
+ if (commit(handle, fsp, fsp->fh->pos, ret) == -1) {
+ return -1;
+ }
+ }
return ret;
}
@@ -151,20 +256,45 @@ static ssize_t commit_pwrite(
ssize_t ret;
ret = SMB_VFS_NEXT_PWRITE(handle, fsp, fd, data, count, offset);
- commit(handle, fsp, ret);
+ if (ret > 0) {
+ if (commit(handle, fsp, offset, ret) == -1) {
+ return -1;
+ }
+ }
return ret;
}
-static ssize_t commit_close(
+static int commit_close(
vfs_handle_struct * handle,
files_struct * fsp,
int fd)
{
+ /* Commit errors not checked, close() will find them again */
commit_all(handle, fsp);
return SMB_VFS_NEXT_CLOSE(handle, fsp, fd);
}
+static int commit_ftruncate(
+ vfs_handle_struct * handle,
+ files_struct * fsp,
+ int fd,
+ SMB_OFF_T len)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, fd, len);
+ if (result == 0) {
+ struct commit_info *c;
+ if ((c = VFS_FETCH_FSP_EXTENSION(handle, fsp))) {
+ commit(handle, fsp, len, 0);
+ c->eof = len;
+ }
+ }
+
+ return result;
+}
+
static vfs_op_tuple commit_ops [] =
{
{SMB_VFS_OP(commit_open),
@@ -177,6 +307,8 @@ static vfs_op_tuple commit_ops [] =
SMB_VFS_OP_PWRITE, SMB_VFS_LAYER_TRANSPARENT},
{SMB_VFS_OP(commit_connect),
SMB_VFS_OP_CONNECT, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(commit_ftruncate),
+ SMB_VFS_OP_FTRUNCATE, SMB_VFS_LAYER_TRANSPARENT},
{SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
};
@@ -187,3 +319,4 @@ NTSTATUS vfs_commit_init(void)
return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, MODULE, commit_ops);
}
+