summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source3/param/loadparm.c4
-rw-r--r--source3/smbd/trans2.c54
-rw-r--r--source3/smbd/vfs.c81
3 files changed, 127 insertions, 12 deletions
diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c
index a1871437ac..e4cd3a7982 100644
--- a/source3/param/loadparm.c
+++ b/source3/param/loadparm.c
@@ -363,6 +363,7 @@ typedef struct
BOOL bWidelinks;
BOOL bSymlinks;
BOOL bSyncAlways;
+ BOOL bStrictAllocate;
BOOL bStrictSync;
char magic_char;
BOOL *copymap;
@@ -476,6 +477,7 @@ static service sDefault = {
True, /* bWidelinks */
True, /* bSymlinks */
False, /* bSyncAlways */
+ False, /* bStrictAllocate */
False, /* bStrictSync */
'~', /* magic char */
NULL, /* copymap */
@@ -798,6 +800,7 @@ static struct parm_struct parm_table[] = {
{"socket options", P_GSTRING, P_GLOBAL, user_socket_options, NULL, NULL, 0},
{"stat cache size", P_INTEGER, P_GLOBAL, &Globals.stat_cache_size, NULL, NULL, 0},
+ {"strict allocate", P_BOOL, P_LOCAL, &sDefault.bStrictAllocate, NULL, NULL, FLAG_SHARE},
{"strict sync", P_BOOL, P_LOCAL, &sDefault.bStrictSync, NULL, NULL, FLAG_SHARE},
{"sync always", P_BOOL, P_LOCAL, &sDefault.bSyncAlways, NULL, NULL, FLAG_SHARE},
{"use mmap", P_BOOL, P_GLOBAL, &Globals.bUseMmap, NULL, NULL, 0},
@@ -1640,6 +1643,7 @@ FN_LOCAL_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_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/trans2.c b/source3/smbd/trans2.c
index ae312cd5d2..bb7ca6e0f8 100644
--- a/source3/smbd/trans2.c
+++ b/source3/smbd/trans2.c
@@ -1878,25 +1878,59 @@ static int call_trans2setfilepathinfo(connection_struct *conn,
break;
}
- /*
- * NT seems to use this call with a size of zero
- * to mean truncate the file. JRA.
- */
-
case 1019:
case 1020:
case SMB_SET_FILE_ALLOCATION_INFO:
{
- SMB_OFF_T newsize = IVAL(pdata,0);
+ int ret = -1;
+ size = IVAL(pdata,0);
#ifdef LARGE_SMB_OFF_T
- newsize |= (((SMB_OFF_T)IVAL(pdata,4)) << 32);
+ size |= (((SMB_OFF_T)IVAL(pdata,4)) << 32);
#else /* LARGE_SMB_OFF_T */
if (IVAL(pdata,4) != 0) /* more than 32 bits? */
return(ERROR(ERRDOS,ERRunknownlevel));
#endif /* LARGE_SMB_OFF_T */
- DEBUG(10,("call_trans2setfilepathinfo: Set file allocation info for file %s to %.0f\n", fname, (double)newsize ));
- if(newsize == 0)
- size = 0;
+ DEBUG(10,("call_trans2setfilepathinfo: Set file allocation info for file %s to %.0f\n",
+ fname, (double)size ));
+
+ if(size != sbuf.st_size) {
+
+ DEBUG(10,("call_trans2setfilepathinfo: file %s : setting new size to %.0f\n",
+ fname, (double)size ));
+
+ if (fd == -1) {
+ files_struct *new_fsp = NULL;
+ int access_mode = 0;
+ int action = 0;
+
+ if(global_oplock_break) {
+ /* Queue this file modify as we are the process of an oplock break. */
+
+ DEBUG(2,("call_trans2setfilepathinfo: queueing message due to being "));
+ DEBUGADD(2,( "in oplock break state.\n"));
+
+ push_oplock_pending_smb_message(inbuf, length);
+ return -1;
+ }
+
+ new_fsp = open_file_shared(conn, fname, &sbuf,
+ SET_OPEN_MODE(DOS_OPEN_RDWR),
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
+ 0, 0, &access_mode, &action);
+
+ if (new_fsp == NULL)
+ return(UNIXERROR(ERRDOS,ERRbadpath));
+ ret = vfs_allocate_file_space(new_fsp, size);
+ close_file(new_fsp,True);
+ } else {
+ ret = vfs_allocate_file_space(fsp, size);
+ }
+ }
+
+ if (ret == -1)
+ return(UNIXERROR(ERRHRD,ERRdiskfull));
+
+ sbuf.st_size = size;
break;
}
diff --git a/source3/smbd/vfs.c b/source3/smbd/vfs.c
index 50361564a5..834d75266e 100644
--- a/source3/smbd/vfs.c
+++ b/source3/smbd/vfs.c
@@ -298,6 +298,84 @@ ssize_t vfs_write_data(files_struct *fsp,const char *buffer,size_t N)
}
/****************************************************************************
+ An allocate file space call using the vfs interface.
+ Allocates space for a file from a filedescriptor.
+ Returns 0 on success, -1 on failure.
+****************************************************************************/
+
+int vfs_allocate_file_space(files_struct *fsp, SMB_OFF_T len)
+{
+ int ret;
+ SMB_STRUCT_STAT st;
+ struct vfs_ops *vfs_ops = &fsp->conn->vfs_ops;
+
+ if (!lp_strict_allocate(SNUM(fsp->conn)))
+ return vfs_set_filelen(fsp, len);
+
+ release_level_2_oplocks_on_change(fsp);
+
+ /*
+ * Actually try and commit the space on disk....
+ */
+
+ DEBUG(10,("vfs_allocate_file_space: file %s, len %.0f\n", fsp->fsp_name, (double)len ));
+
+ ret = vfs_fstat(fsp,fsp->fd,&st);
+ if (ret == -1)
+ return ret;
+
+ if (len == st.st_size)
+ return 0;
+
+ if (len < st.st_size) {
+ /* Shrink - use ftruncate. */
+
+ DEBUG(10,("vfs_allocate_file_space: file %s, shrink. Current size %.0f\n",
+ fsp->fsp_name, (double)st.st_size ));
+
+ if ((ret = vfs_ops->ftruncate(fsp, fsp->fd, len)) != -1) {
+ set_filelen_write_cache(fsp, len);
+ }
+ return ret;
+ }
+
+ /* Grow - we need to write out the space.... */
+ {
+ static unsigned char zero_space[65536];
+
+ SMB_OFF_T start_pos = st.st_size;
+ SMB_OFF_T len_to_write = len - st.st_size;
+ SMB_OFF_T retlen;
+
+ DEBUG(10,("vfs_allocate_file_space: file %s, grow. Current size %.0f\n",
+ fsp->fsp_name, (double)st.st_size ));
+
+ if ((retlen = vfs_ops->lseek(fsp, fsp->fd, start_pos, SEEK_SET)) != start_pos)
+ return -1;
+
+ while ( len_to_write > 0) {
+ SMB_OFF_T current_len_to_write = MIN(sizeof(zero_space),len_to_write);
+
+ retlen = vfs_ops->write(fsp,fsp->fd,zero_space,current_len_to_write);
+ if (retlen != current_len_to_write) {
+ /* Write fail - return to original size. */
+ int save_errno = errno;
+ fsp->conn->vfs_ops.ftruncate(fsp, fsp->fd, st.st_size);
+ errno = save_errno;
+ return -1;
+ }
+
+ DEBUG(10,("vfs_allocate_file_space: file %s, grow. wrote %.0f\n",
+ fsp->fsp_name, (double)current_len_to_write ));
+
+ len_to_write -= current_len_to_write;
+ }
+ set_filelen_write_cache(fsp, len);
+ }
+ return 0;
+}
+
+/****************************************************************************
A vfs set_filelen call.
set the length of a file from a filedescriptor.
Returns 0 on success, -1 on failure.
@@ -308,9 +386,8 @@ int vfs_set_filelen(files_struct *fsp, SMB_OFF_T len)
int ret;
release_level_2_oplocks_on_change(fsp);
- if ((ret = fsp->conn->vfs_ops.ftruncate(fsp, fsp->fd, len)) != -1) {
+ if ((ret = fsp->conn->vfs_ops.ftruncate(fsp, fsp->fd, len)) != -1)
set_filelen_write_cache(fsp, len);
- }
return ret;
}