From e660918efd611738d253389fd3f3da5885cd60e3 Mon Sep 17 00:00:00 2001
From: Olaf Flebbe <o.flebbe@science-computing.de>
Date: Mon, 30 Nov 2009 17:20:46 +0100
Subject: [PATCH] try to avoid fragmentation

---
 source3/include/local.h       |    4 +++
 source3/include/proto.h       |    4 +-
 source3/include/smb.h         |    6 +++++
 source3/modules/vfs_default.c |    6 ++++-
 source3/param/loadparm.c      |   27 +++++++++++++++++++-----
 source3/smbd/aio.c            |   45 ++++++++++++++++++++++++++++++++++++++++-
 source3/smbd/fileio.c         |    5 ++-
 source3/smbd/vfs.c            |   13 ++++++++++-
 8 files changed, 96 insertions(+), 14 deletions(-)

diff --git a/source3/include/local.h b/source3/include/local.h
index 45767ad..a27c258 100644
--- a/source3/include/local.h
+++ b/source3/include/local.h
@@ -253,6 +253,10 @@
 /* Windows minimum lock resolution timeout in ms */
 #define WINDOWS_MINIMUM_LOCK_TIMEOUT_MS 200
 
+/* When strict allocate = partial, define the limit on how far
+ * ahead we will write. */
+#define STRICT_ALLOCATE_PARTIAL_LIMIT 0x200000
+
 /* Maximum size of RPC data we will accept for one call. */
 #define MAX_RPC_DATA_SIZE (15*1024*1024)
 
diff --git a/source3/include/proto.h b/source3/include/proto.h
index d2ae62c..305b6d8 100644
--- a/source3/include/proto.h
+++ b/source3/include/proto.h
@@ -4175,7 +4175,7 @@ bool lp_manglednames(const struct share_params *p );
 bool lp_widelinks(int );
 bool lp_symlinks(int );
 bool lp_syncalways(int );
-bool lp_strict_allocate(int );
+int lp_strict_allocate(int );
 bool lp_strict_sync(int );
 bool lp_map_system(int );
 bool lp_delete_readonly(int );
@@ -7131,7 +7131,7 @@ ssize_t vfs_pwrite_data(struct smb_request *req,
 			SMB_OFF_T offset);
 int vfs_allocate_file_space(files_struct *fsp, uint64_t len);
 int vfs_set_filelen(files_struct *fsp, SMB_OFF_T len);
-int vfs_fill_sparse(files_struct *fsp, SMB_OFF_T len);
+int vfs_fill_sparse(files_struct *fsp, SMB_OFF_T len, enum smb_strict_allocate_options sa_options);
 SMB_OFF_T vfs_transfer_file(files_struct *in, files_struct *out, SMB_OFF_T n);
 char *vfs_readdirname(connection_struct *conn, void *p, SMB_STRUCT_STAT *sbuf);
 int vfs_ChDir(connection_struct *conn, const char *path);
diff --git a/source3/include/smb.h b/source3/include/smb.h
index 2a3c455..79d87e4 100644
--- a/source3/include/smb.h
+++ b/source3/include/smb.h
@@ -1940,4 +1940,10 @@ struct smb_file_time {
 	struct timespec create_time;
 };
 
+enum smb_strict_allocate_options {
+	STRICT_ALLOCATE_OFF=0,
+	STRICT_ALLOCATE_ON=1,
+	STRICT_ALLOCATE_PARTIAL=2
+};
+
 #endif /* _SMB_H */
diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c
index f5cee4c..75a532a 100644
--- a/source3/modules/vfs_default.c
+++ b/source3/modules/vfs_default.c
@@ -884,10 +884,14 @@ static int vfswrap_ftruncate(vfs_handle_struct *handle, files_struct *fsp, SMB_O
 	SMB_STRUCT_STAT st;
 	char c = 0;
 	SMB_OFF_T currpos;
+	enum smb_strict_allocate_options sa_options = lp_strict_allocate(SNUM(fsp->conn));
 
 	START_PROFILE(syscall_ftruncate);
 
-	if (lp_strict_allocate(SNUM(fsp->conn))) {
+	/* Only use allocation truncate if strict allocate
+ 	 * is set "on", not off or partial.
+ 	 */
+	if (sa_options == STRICT_ALLOCATE_ON) {
 		result = strict_allocate_ftruncate(handle, fsp, len);
 		END_PROFILE(syscall_ftruncate);
 		return result;
diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c
index cd27fe4..c69bc6b 100644
--- a/source3/param/loadparm.c
+++ b/source3/param/loadparm.c
@@ -457,7 +457,7 @@ struct service {
 	bool bWidelinks;
 	bool bSymlinks;
 	bool bSyncAlways;
-	bool bStrictAllocate;
+	int iStrictAllocate;
 	bool bStrictSync;
 	char magic_char;
 	struct bitmap *copymap;
@@ -601,7 +601,7 @@ static struct service sDefault = {
 	True,			/* bWidelinks */
 	True,			/* bSymlinks */
 	False,			/* bSyncAlways */
-	False,			/* bStrictAllocate */
+	STRICT_ALLOCATE_OFF,	/* iStrictAllocate */
 	False,			/* bStrictSync */
 	'~',			/* magic char */
 	NULL,			/* copymap */
@@ -882,6 +882,21 @@ static const struct enum_list enum_kerberos_method[] = {
 	{-1, NULL}
 };
 
+static const struct enum_list enum_strict_allocate[] = {
+	{STRICT_ALLOCATE_OFF, "No"},
+	{STRICT_ALLOCATE_OFF, "False"},
+	{STRICT_ALLOCATE_OFF, "0"},
+	{STRICT_ALLOCATE_OFF, "Off"},
+	{STRICT_ALLOCATE_OFF, "disabled"},
+	{STRICT_ALLOCATE_ON, "Yes"},
+	{STRICT_ALLOCATE_ON, "True"},
+	{STRICT_ALLOCATE_ON, "1"},
+	{STRICT_ALLOCATE_ON, "On"},
+	{STRICT_ALLOCATE_ON, "enabled"},
+	{STRICT_ALLOCATE_PARTIAL, "partial"},
+	{-1, NULL}
+};
+
 /* Note: We do not initialise the defaults union - it is not allowed in ANSI C
  *
  * The FLAG_HIDE is explicit. Parameters set this way do NOT appear in any edit
@@ -2434,11 +2449,11 @@ static struct parm_struct parm_table[] = {
 	},
 	{
 		.label		= "strict allocate",
-		.type		= P_BOOL,
+		.type		= P_ENUM,
 		.p_class	= P_LOCAL,
-		.ptr		= &sDefault.bStrictAllocate,
+		.ptr		= &sDefault.iStrictAllocate,
 		.special	= NULL,
-		.enum_list	= NULL,
+		.enum_list	= enum_strict_allocate,
 		.flags		= FLAG_ADVANCED | FLAG_SHARE,
 	},
 	{
@@ -5548,7 +5563,7 @@ FN_LOCAL_PARM_BOOL(lp_manglednames, bMangledNames)
 FN_LOCAL_BOOL(lp_widelinks, bWidelinks)
 FN_LOCAL_BOOL(lp_symlinks, bSymlinks)
 FN_LOCAL_BOOL(lp_syncalways, bSyncAlways)
-FN_LOCAL_BOOL(lp_strict_allocate, bStrictAllocate)
+FN_LOCAL_INTEGER(lp_strict_allocate, iStrictAllocate)
 FN_LOCAL_BOOL(lp_strict_sync, bStrictSync)
 FN_LOCAL_BOOL(lp_map_system, bMap_system)
 FN_LOCAL_BOOL(lp_delete_readonly, bDeleteReadonly)
diff --git a/source3/smbd/aio.c b/source3/smbd/aio.c
index 406ec1b..0bc8c26 100644
--- a/source3/smbd/aio.c
+++ b/source3/smbd/aio.c
@@ -209,10 +209,12 @@ bool schedule_aio_write_and_X(connection_struct *conn,
 			      SMB_OFF_T startpos,
 			      size_t numtowrite)
 {
-	struct aio_extra *aio_ex;
+  struct aio_extra *aio_ex;
 	SMB_STRUCT_AIOCB *a;
+	SMB_STRUCT_STAT st;
 	size_t bufsize;
 	bool write_through = BITSETW(req->vwv+7,0);
+	bool other_aio_found;
 	size_t min_aio_write_size = lp_aio_write_size(SNUM(conn));
 	int ret;
 
@@ -251,6 +253,47 @@ bool schedule_aio_write_and_X(connection_struct *conn,
 		return False;
 	}
 
+
+	other_aio_found = false;
+	/* Make sure there are no outstanding aio writes on same fsp */
+	for( aio_ex = aio_list_head; aio_ex; aio_ex = aio_ex->next) {
+		if (fsp == aio_ex->fsp) {
+		  /* Don't do sparse file detection if other aio statements */
+		  /* are outstanding, this should not happen!               */
+		  other_aio_found = true;
+		  break;
+		}
+	}
+       
+        if (!other_aio_found) {
+	        ret = SMB_VFS_FSTAT(fsp, &st);
+		if (ret == -1) {
+		  /* This should not happen, fall back to synchronous */
+		  return False;
+		}
+
+
+		if (startpos > st.st_size) {
+		  /* Writing beyond end of file, potentially creating a gap */
+
+		  enum smb_strict_allocate_options sa_options = lp_strict_allocate(SNUM(fsp->conn));
+		  
+		  /* let the synchronous write handle filling the sparse file */
+		  if (sa_options == STRICT_ALLOCATE_ON)
+		    return False;
+
+		  /* If strict allocate is set to "partial", ignore all allocate
+		   * requests over the STRICT_ALLOCATE_PARTIAL_LIMIT. */
+		  
+		  if ((sa_options == STRICT_ALLOCATE_PARTIAL) &&
+		      (startpos - st.st_size < STRICT_ALLOCATE_PARTIAL_LIMIT)) {
+		    return False;
+		  }
+		}
+	}
+
+
+
 	bufsize = smb_size + 6*2;
 
 	if (!(aio_ex = create_aio_extra(fsp, bufsize))) {
diff --git a/source3/smbd/fileio.c b/source3/smbd/fileio.c
index adf664b..73d70c6 100644
--- a/source3/smbd/fileio.c
+++ b/source3/smbd/fileio.c
@@ -125,9 +125,10 @@ static ssize_t real_write_file(struct smb_request *req,
         if (pos == -1) {
                 ret = vfs_write_data(req, fsp, data, n);
         } else {
+		enum smb_strict_allocate_options sa_options = lp_strict_allocate(SNUM(fsp->conn));
 		fsp->fh->pos = pos;
-		if (pos && lp_strict_allocate(SNUM(fsp->conn))) {
-			if (vfs_fill_sparse(fsp, pos) == -1) {
+		if (pos && (sa_options != STRICT_ALLOCATE_OFF)) {
+			if (vfs_fill_sparse(fsp, pos, sa_options) == -1) {
 				return -1;
 			}
 		}
diff --git a/source3/smbd/vfs.c b/source3/smbd/vfs.c
index 4267728..c78b258 100644
--- a/source3/smbd/vfs.c
+++ b/source3/smbd/vfs.c
@@ -556,8 +556,9 @@ int vfs_allocate_file_space(files_struct *fsp, uint64_t len)
 	contend_level2_oplocks_begin(fsp, LEVEL2_CONTEND_ALLOC_GROW);
 	contend_level2_oplocks_end(fsp, LEVEL2_CONTEND_ALLOC_GROW);
 
-	if (!lp_strict_allocate(SNUM(fsp->conn)))
+	if (lp_strict_allocate(SNUM(fsp->conn)) == STRICT_ALLOCATE_OFF) {
 		return 0;
+	}
 
 	len -= st.st_size;
 	len /= 1024; /* Len is now number of 1k blocks needed. */
@@ -613,7 +614,7 @@ int vfs_set_filelen(files_struct *fsp, SMB_OFF_T len)
 
 #define SPARSE_BUF_WRITE_SIZE (32*1024)
 
-int vfs_fill_sparse(files_struct *fsp, SMB_OFF_T len)
+int vfs_fill_sparse(files_struct *fsp, SMB_OFF_T len, enum smb_strict_allocate_options sa_options)
 {
 	int ret;
 	SMB_STRUCT_STAT st;
@@ -631,6 +632,14 @@ int vfs_fill_sparse(files_struct *fsp, SMB_OFF_T len)
 		return 0;
 	}
 
+	/* If strict allocate is set to "partial", ignore all allocate
+ 	 * retquests over the STRICT_ALLOCATE_PARTIAL_LIMIT. */
+
+	if ((sa_options == STRICT_ALLOCATE_PARTIAL) &&
+			(len - st.st_size > STRICT_ALLOCATE_PARTIAL_LIMIT)) {
+		return 0;
+	}
+
 	DEBUG(10,("vfs_fill_sparse: write zeros in file %s from len %.0f to len %.0f (%.0f bytes)\n",
 		fsp->fsp_name, (double)st.st_size, (double)len, (double)(len - st.st_size)));
 
-- 
1.6.0.2

