From f4d7222e6c0728a301f09dcee5253de1adb271e7 Mon Sep 17 00:00:00 2001
From: Olaf <of@oflebbe.de>
Date: Mon, 8 Feb 2010 15:48:56 +0100
Subject: [PATCH] Implement a new vfs call fallocate in order to support efficient allocation of
 disk space with sys_fallocate

---
 source3/include/vfs.h            |   25 +++++++----
 source3/include/vfs_macros.h     |    1 +
 source3/modules/vfs_default.c    |   85 ++++++++++++++++++++++++++++++++++++++
 source3/modules/vfs_full_audit.c |   16 +++++++
 4 files changed, 117 insertions(+), 10 deletions(-)

diff --git a/source3/include/vfs.h b/source3/include/vfs.h
index 0c0e093..843625b 100644
--- a/source3/include/vfs.h
+++ b/source3/include/vfs.h
@@ -1,21 +1,21 @@
-/* 
+/*
    Unix SMB/CIFS implementation.
    VFS structures and parameters
    Copyright (C) Jeremy Allison                         1999-2005
    Copyright (C) Tim Potter				1999
    Copyright (C) Alexander Bokovoy			2002-2005
    Copyright (C) Stefan (metze) Metzmacher		2003
-   
+
    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
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
-   
+
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
-   
+
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
@@ -55,16 +55,16 @@
 /* Changed to version 9 to include the get_shadow_data call. --metze */
 /* Changed to version 10 to include pread/pwrite calls. */
 /* Changed to version 11 to include seekdir/telldir/rewinddir calls. JRA */
-/* Changed to version 12 to add mask and attributes to opendir(). JRA 
+/* Changed to version 12 to add mask and attributes to opendir(). JRA
    Also include aio calls. JRA. */
 /* Changed to version 13 as the internal structure of files_struct has changed. JRA */
 /* Changed to version 14 as we had to change DIR to SMB_STRUCT_DIR. JRA */
 /* Changed to version 15 as we added the statvfs call. JRA */
 /* Changed to version 16 as we added the getlock call. JRA */
 /* Changed to version 17 as we removed redundant connection_struct parameters. --jpeach */
-/* Changed to version 18 to add fsp parameter to the open call -- jpeach 
+/* Changed to version 18 to add fsp parameter to the open call -- jpeach
    Also include kernel_flock call - jmcd */
-/* Changed to version 19, kernel change notify has been merged 
+/* Changed to version 19, kernel change notify has been merged
    Also included linux setlease call - jmcd */
 /* Changed to version 20, use ntimes call instead of utime (greater
  * timestamp resolition. JRA. */
@@ -117,8 +117,9 @@
 /* Leave at 25 - not yet released. Add init_search_op call. - sdann */
 /* Leave at 25 - not yet released. Add locking calls. -- zkirsch. */
 /* Leave at 25 - not yet released. Add strict locking calls. -- drichards. */
+/* Changed to 26          - add fallocate: oflebbe */
 
-#define SMB_VFS_INTERFACE_VERSION 25
+#define SMB_VFS_INTERFACE_VERSION 26
 
 
 /* to bug old modules which are trying to compile with the old functions */
@@ -207,6 +208,7 @@ typedef enum _vfs_op_type {
 	SMB_VFS_OP_GETWD,
 	SMB_VFS_OP_NTIMES,
 	SMB_VFS_OP_FTRUNCATE,
+	SMB_VFS_OP_FALLOCATE,
 	SMB_VFS_OP_LOCK,
 	SMB_VFS_OP_KERNEL_FLOCK,
 	SMB_VFS_OP_LINUX_SETLEASE,
@@ -369,6 +371,8 @@ struct vfs_ops {
 		char *(*getwd)(struct vfs_handle_struct *handle, char *buf);
 		int (*ntimes)(struct vfs_handle_struct *handle, const char *path, struct smb_file_time *ft);
 		int (*ftruncate)(struct vfs_handle_struct *handle, struct files_struct *fsp, SMB_OFF_T offset);
+		int (*fallocate)(struct vfs_handle_struct *handle, struct files_struct *fsp, SMB_OFF_T offset,
+				 SMB_OFF_T len);
 		bool (*lock)(struct vfs_handle_struct *handle, struct files_struct *fsp, int op, SMB_OFF_T offset, SMB_OFF_T count, int type);
 		int (*kernel_flock)(struct vfs_handle_struct *handle, struct files_struct *fsp, uint32 share_mode);
 		int (*linux_setlease)(struct vfs_handle_struct *handle, struct files_struct *fsp, int leasetype);
@@ -381,7 +385,7 @@ struct vfs_ops {
 		NTSTATUS (*notify_watch)(struct vfs_handle_struct *handle,
 					 struct sys_notify_context *ctx,
 					 struct notify_entry *e,
-					 void (*callback)(struct sys_notify_context *ctx, 
+					 void (*callback)(struct sys_notify_context *ctx,
 							  void *private_data,
 							  struct notify_event *ev),
 					 void *private_data, void *handle_p);
@@ -550,6 +554,7 @@ struct vfs_ops {
 		struct vfs_handle_struct *getwd;
 		struct vfs_handle_struct *ntimes;
 		struct vfs_handle_struct *ftruncate;
+		struct vfs_handle_struct *fallocate;
 		struct vfs_handle_struct *lock;
 		struct vfs_handle_struct *kernel_flock;
 		struct vfs_handle_struct *linux_setlease;
@@ -646,7 +651,7 @@ struct vfs_ops {
     which implement actual file system calls (like DB-based VFS). For example,
     default POSIX VFS which is built in into Samba is an opaque VFS module.
 
-    Other layer types (audit, splitter, scanner) were designed to provide different 
+    Other layer types (audit, splitter, scanner) were designed to provide different
     degree of transparency and for diagnosing VFS module behaviour.
 
     Each module can implement several layers at the same time provided that only
diff --git a/source3/include/vfs_macros.h b/source3/include/vfs_macros.h
index acb158e..bbb2758 100644
--- a/source3/include/vfs_macros.h
+++ b/source3/include/vfs_macros.h
@@ -74,6 +74,7 @@
 #define SMB_VFS_GETWD(conn, buf) ((conn)->vfs.ops.getwd((conn)->vfs.handles.getwd, (buf)))
 #define SMB_VFS_NTIMES(conn, path, ts) ((conn)->vfs.ops.ntimes((conn)->vfs.handles.ntimes, (path), (ts)))
 #define SMB_VFS_FTRUNCATE(fsp, offset) ((fsp)->conn->vfs.ops.ftruncate((fsp)->conn->vfs.handles.ftruncate, (fsp), (offset)))
+#define SMB_VFS_FALLOCATE(fsp, offset, len) ((fsp)->conn->vfs.ops.fallocate((fsp)->conn->vfs_handles, (fsp), (offset), (len)))
 #define SMB_VFS_LOCK(fsp, op, offset, count, type) ((fsp)->conn->vfs.ops.lock((fsp)->conn->vfs.handles.lock, (fsp), (op), (offset), (count), (type)))
 #define SMB_VFS_KERNEL_FLOCK(fsp, share_mode) ((fsp)->conn->vfs.ops.kernel_flock((fsp)->conn->vfs.handles.kernel_flock, (fsp), (share_mode)))
 #define SMB_VFS_LINUX_SETLEASE(fsp, leasetype) ((fsp)->conn->vfs.ops.linux_setlease((fsp)->conn->vfs.handles.linux_setlease, (fsp), (leasetype)))
diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c
index 4b6c6a8..bcbff82 100644
--- a/source3/modules/vfs_default.c
+++ b/source3/modules/vfs_default.c
@@ -968,6 +968,89 @@ static int vfswrap_ftruncate(vfs_handle_struct *handle, files_struct *fsp, SMB_O
 	return result;
 }
 
+static int vfswrap_fallocate(vfs_handle_struct *handle, files_struct *fsp, SMB_OFF_T offset, SMB_OFF_T len)
+{
+	int result = -1;
+	SMB_STRUCT_STAT st;
+	char c = 0;
+	SMB_OFF_T currpos;
+	unsigned char zero_space[4096];
+	SMB_OFF_T space_to_write;
+	SMB_OFF_T total, pwrite_ret;
+
+	START_PROFILE(syscall_fallocate);
+
+	result = sys_posix_fallocate(fsp->fh->fd, offset, len);
+	if (result == 0)
+		goto done;
+
+	/* double check for invalid parameters */
+	if (len <= 0 || offset < 0) {
+		result = 0;
+		goto done;
+	}
+
+	if (SMB_VFS_FSTAT(fsp, &st) == -1)
+		goto done;
+
+#ifdef S_ISFIFO
+	if (S_ISFIFO(st.st_mode)) {
+		result = 0;
+		goto done;
+	}
+#endif
+
+	/* be cautious! if file already larger: NOP */
+	if (st.st_size >= offset + len) {
+		result = 0;
+		goto done;
+	}
+
+	/* check if offset is within the current file */
+	if (st.st_size > offset) {
+		/* already checked, len could not be negative */
+		len -= (st.st_size - offset) - 1;
+		/* position one byte behind eof */
+		offset = st.st_size + 1;
+	}
+
+	currpos = SMB_VFS_LSEEK(fsp, 0, SEEK_CUR);
+	if (currpos == -1) {
+		goto done;
+	}
+
+	total = 0;
+	memset(zero_space, '\0', sizeof(zero_space));
+	while (total < len) {
+		SMB_OFF_T retlen;
+		SMB_OFF_T current_len_to_write = MIN(sizeof(zero_space), (len - total));
+
+		retlen = SMB_VFS_PWRITE(fsp, (char *) zero_space, current_len_to_write, offset + total);
+		if (retlen == -1) {
+			DEBUG(10, ("vfs_fill_sparse: SMB_VFS_PWRITE for file "
+				  "failed with error %s\n",
+				  strerror(errno)));
+			result = -1;
+			goto done;
+		}
+		if (retlen == 0) {
+			result = 0;
+			goto done;
+		}
+		total += retlen;
+	}
+
+	/* Seek to where we were */
+	if (SMB_VFS_LSEEK(fsp, currpos, SEEK_SET) != currpos)
+		goto done;
+	result = 0;
+
+  done:
+
+	END_PROFILE(syscall_fallocate);
+	return result;
+}
+
 static bool vfswrap_lock(vfs_handle_struct *handle, files_struct *fsp, int op, SMB_OFF_T offset, SMB_OFF_T count, int type)
 {
 	bool result;
@@ -1669,6 +1752,8 @@ static vfs_op_tuple vfs_default_ops[] = {
 	 SMB_VFS_LAYER_OPAQUE},
 	{SMB_VFS_OP(vfswrap_ftruncate),	SMB_VFS_OP_FTRUNCATE,
 	 SMB_VFS_LAYER_OPAQUE},
+	{SMB_VFS_OP(vfswrap_fallocate),	SMB_VFS_OP_FALLOCATE,
+	 SMB_VFS_LAYER_OPAQUE},
 	{SMB_VFS_OP(vfswrap_lock),	SMB_VFS_OP_LOCK,
 	 SMB_VFS_LAYER_OPAQUE},
 	{SMB_VFS_OP(vfswrap_kernel_flock),	SMB_VFS_OP_KERNEL_FLOCK,
diff --git a/source3/modules/vfs_full_audit.c b/source3/modules/vfs_full_audit.c
index ebe89ec..1a536d8 100644
--- a/source3/modules/vfs_full_audit.c
+++ b/source3/modules/vfs_full_audit.c
@@ -180,6 +180,8 @@ static int smb_full_audit_ntimes(vfs_handle_struct *handle,
 		       const char *path, struct smb_file_time *ft);
 static int smb_full_audit_ftruncate(vfs_handle_struct *handle, files_struct *fsp,
 			   SMB_OFF_T len);
+static int smb_full_audit_fallocate(vfs_handle_struct *handle, files_struct *fsp,
+			   SMB_OFF_T offset, SMB_OFF_T len);
 static bool smb_full_audit_lock(vfs_handle_struct *handle, files_struct *fsp,
 		       int op, SMB_OFF_T offset, SMB_OFF_T count, int type);
 static int smb_full_audit_kernel_flock(struct vfs_handle_struct *handle,
@@ -653,6 +655,7 @@ static struct {
 	{ SMB_VFS_OP_GETWD,	"getwd" },
 	{ SMB_VFS_OP_NTIMES,	"ntimes" },
 	{ SMB_VFS_OP_FTRUNCATE,	"ftruncate" },
+	{ SMB_VFS_OP_FALLOCATE,	"fallocate" },
 	{ SMB_VFS_OP_LOCK,	"lock" },
 	{ SMB_VFS_OP_KERNEL_FLOCK,	"kernel_flock" },
 	{ SMB_VFS_OP_LINUX_SETLEASE, "linux_setlease" },
@@ -1533,6 +1536,19 @@ static int smb_full_audit_ftruncate(vfs_handle_struct *handle, files_struct *fsp
 	return result;
 }
 
+static int smb_full_audit_fallocate(vfs_handle_struct *handle, files_struct *fsp,
+			 SMB_OFF_T offset, SMB_OFF_T len)
+{
+	int result;
+
+	result = SMB_VFS_NEXT_FALLOCATE(handle, fsp, offset, len);
+
+	do_log(SMB_VFS_OP_FALLOCATE, (result >= 0), handle,
+	       "%s", fsp->fsp_name);
+
+	return result;
+}
+
 static bool smb_full_audit_lock(vfs_handle_struct *handle, files_struct *fsp,
 		       int op, SMB_OFF_T offset, SMB_OFF_T count, int type)
 {
-- 
1.6.0.2

