From f0335b9a86011ab981a85dd0687f80e44f1ad7c3 Mon Sep 17 00:00:00 2001 From: Olaf Flebbe Date: Tue, 1 Dec 2009 15:37:46 +0100 Subject: [PATCH] Do not create fragemented files --- 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 | 47 ++++++++++++++++++++++++++++++++++++++++- source3/smbd/fileio.c | 5 ++- source3/smbd/vfs.c | 13 +++++++++- 8 files changed, 98 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..26da370 100644 --- a/source3/smbd/aio.c +++ b/source3/smbd/aio.c @@ -211,8 +211,10 @@ bool schedule_aio_write_and_X(connection_struct *conn, { 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; @@ -233,7 +235,7 @@ bool schedule_aio_write_and_X(connection_struct *conn, } /* Only do this on non-chained and non-chaining reads not using the - * write cache. */ + write cache. */ if (req_is_in_chain(req) || (lp_write_cache_size(SNUM(conn)) != 0)) { return False; } @@ -251,6 +253,49 @@ 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 a 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..9969926 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 + * requests 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