From 8998f4b01310e4b45e75d8d5f3260b5ba5c1cdf9 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 20 Dec 2010 16:53:16 -0800 Subject: Added call out to a Linux-compatible fallocate() when we need to extend a file allocation extent without changing end-of-file size. Autobuild-User: Jeremy Allison Autobuild-Date: Tue Dec 21 02:41:24 CET 2010 on sn-devel-104 --- source3/configure.in | 35 +++++++++++++++++++++++++++++++++++ source3/include/proto.h | 1 + source3/lib/system.c | 35 +++++++++++++++++++++++++++++++++++ source3/modules/vfs_default.c | 5 +++-- source3/smbd/vfs.c | 13 ++++++++++++- 5 files changed, 86 insertions(+), 3 deletions(-) diff --git a/source3/configure.in b/source3/configure.in index b43d0b34de..ed99b17191 100644 --- a/source3/configure.in +++ b/source3/configure.in @@ -741,6 +741,7 @@ AC_CHECK_HEADERS(sys/syslog.h syslog.h) AC_CHECK_HEADERS(langinfo.h locale.h) AC_CHECK_HEADERS(xfs/libxfs.h) AC_CHECK_HEADERS(netgroup.h) +AC_CHECK_HEADERS(linux/falloc.h) AC_CHECK_HEADERS(rpcsvc/yp_prot.h,,,[[ #if HAVE_RPC_RPC_H @@ -1095,6 +1096,7 @@ AC_CHECK_FUNCS(sigprocmask sigblock sigaction sigset innetgr setnetgrent getnetg AC_CHECK_FUNCS(initgroups select poll rdchk getgrnam getgrent pathconf) AC_CHECK_FUNCS(setpriv setgidx setuidx setgroups sysconf stat64 fstat64) AC_CHECK_FUNCS(lstat64 fopen64 atexit grantpt lseek64 ftruncate64 posix_fallocate posix_fallocate64) +AC_CHECK_FUNCS(fallocate fallocate64) AC_CHECK_FUNCS(fseek64 fseeko64 ftell64 ftello64 setluid getpwanam) AC_CHECK_FUNCS(opendir64 readdir64 seekdir64 telldir64 rewinddir64 closedir64) AC_CHECK_FUNCS(getpwent_r) @@ -2563,6 +2565,39 @@ fi fi # end utmp details +AC_CACHE_CHECK([for linux fallocate],samba_cv_HAVE_LINUX_FALLOCATE,[ +AC_TRY_COMPILE([ +#if defined(HAVE_UNISTD_H) +#include +#endif +#include +#define _GNU_SOURCE +#include +#if defined(HAVE_LINUX_FALLOC_H) +#include +#endif], +[int ret = fallocate(0, FALLOC_FL_KEEP_SIZE, 0, 10);], +samba_cv_HAVE_LINUX_FALLOCATE=yes,samba_cv_HAVE_LINUX_FALLOCATE=no)]) +if test x"$samba_cv_HAVE_LINUX_FALLOCATE" = x"yes" && test x"$ac_cv_func_fallocate" = x"yes"; then + AC_DEFINE(HAVE_LINUX_FALLOCATE,1,[Whether the Linux 'fallocate' function is available]) +fi + +AC_CACHE_CHECK([for linux fallocate64],samba_cv_HAVE_LINUX_FALLOCATE64,[ +AC_TRY_COMPILE([ +#if defined(HAVE_UNISTD_H) +#include +#endif +#include +#define _GNU_SOURCE +#include +#if defined(HAVE_LINUX_FALLOC_H) +#include +#endif], +[int ret = fallocate64(0, FALLOC_FL_KEEP_SIZE, 0, 10);], +samba_cv_HAVE_LINUX_FALLOCATE64=yes,samba_cv_HAVE_LINUX_FALLOCATE64=no)]) +if test x"$samba_cv_HAVE_LINUX_FALLOCATE64" = x"yes" && test x"$ac_cv_func_fallocate64" = x"yes"; then + AC_DEFINE(HAVE_LINUX_FALLOCATE64,1,[Whether the Linux 'fallocate64' function is available]) +fi ICONV_LOOK_DIRS="/usr /usr/local /sw /opt" AC_ARG_WITH(libiconv, diff --git a/source3/include/proto.h b/source3/include/proto.h index dabb315875..566b3f31df 100644 --- a/source3/include/proto.h +++ b/source3/include/proto.h @@ -888,6 +888,7 @@ int sys_lstat(const char *fname,SMB_STRUCT_STAT *sbuf, bool fake_dir_create_times); int sys_ftruncate(int fd, SMB_OFF_T offset); int sys_posix_fallocate(int fd, SMB_OFF_T offset, SMB_OFF_T len); +int sys_fallocate(int fd, enum vfs_fallocate_mode mode, SMB_OFF_T offset, SMB_OFF_T len); SMB_OFF_T sys_lseek(int fd, SMB_OFF_T offset, int whence); int sys_fseek(FILE *fp, SMB_OFF_T offset, int whence); SMB_OFF_T sys_ftell(FILE *fp); diff --git a/source3/lib/system.c b/source3/lib/system.c index 02322b72b5..4cf6a299da 100644 --- a/source3/lib/system.c +++ b/source3/lib/system.c @@ -694,6 +694,41 @@ int sys_posix_fallocate(int fd, SMB_OFF_T offset, SMB_OFF_T len) #endif } +/******************************************************************* + An fallocate() function that matches the semantics of the Linux one. +********************************************************************/ + +#ifdef HAVE_LINUX_FALLOC_H +#include +#endif + +int sys_fallocate(int fd, enum vfs_fallocate_mode mode, SMB_OFF_T offset, SMB_OFF_T len) +{ +#if defined(HAVE_LINUX_FALLOCATE64) || defined(HAVE_LINUX_FALLOCATE) + int lmode; + switch (mode) { + case VFS_FALLOCATE_EXTEND_SIZE: + lmode = 0; + break; + case VFS_FALLOCATE_KEEP_SIZE: + lmode = FALLOC_FL_KEEP_SIZE; + break; + default: + errno = EINVAL; + return -1; + } +#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_LINUX_FALLOCATE64) + return fallocate64(fd, lmode, offset, len); +#elif defined(HAVE_LINUX_FALLOCATE) + return fallocate(fd, lmode, offset, len); +#endif +#else + /* TODO - plumb in fallocate from other filesysetms like VXFS etc. JRA. */ + errno = ENOSYS; + return -1; +#endif +} + /******************************************************************* An ftruncate() wrapper that will deal with 64 bit filesizes. ********************************************************************/ diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c index e08d48ff5f..54f38c3714 100644 --- a/source3/modules/vfs_default.c +++ b/source3/modules/vfs_default.c @@ -965,9 +965,10 @@ static int vfswrap_fallocate(vfs_handle_struct *handle, START_PROFILE(syscall_fallocate); if (mode == VFS_FALLOCATE_EXTEND_SIZE) { result = sys_posix_fallocate(fsp->fh->fd, offset, len); + } else if (mode == VFS_FALLOCATE_KEEP_SIZE) { + result = sys_fallocate(fsp->fh->fd, mode, offset, len); } else { - /* TODO - implement call into Linux fallocate call. */ - errno = ENOSYS; + errno = EINVAL; result = -1; } END_PROFILE(syscall_fallocate); diff --git a/source3/smbd/vfs.c b/source3/smbd/vfs.c index 2ebe2a1062..802639f2fb 100644 --- a/source3/smbd/vfs.c +++ b/source3/smbd/vfs.c @@ -501,13 +501,24 @@ int vfs_allocate_file_space(files_struct *fsp, uint64_t len) return ret; } + if (!lp_strict_allocate(SNUM(fsp->conn))) + return 0; + /* Grow - we need to test if we have enough space. */ contend_level2_oplocks_begin(fsp, LEVEL2_CONTEND_ALLOC_GROW); + + /* See if we have a syscall that will allocate beyond end-of-file + without changing EOF. */ + ret = SMB_VFS_FALLOCATE(fsp, VFS_FALLOCATE_KEEP_SIZE, 0, len); + contend_level2_oplocks_end(fsp, LEVEL2_CONTEND_ALLOC_GROW); - if (!lp_strict_allocate(SNUM(fsp->conn))) + if (ret == 0) { + /* We changed the allocation size on disk, but not + EOF - exactly as required. We're done ! */ return 0; + } len -= fsp->fsp_name->st.st_ex_size; len /= 1024; /* Len is now number of 1k blocks needed. */ -- cgit