diff options
| -rw-r--r-- | source3/param/loadparm.c | 4 | ||||
| -rw-r--r-- | source3/smbd/trans2.c | 54 | ||||
| -rw-r--r-- | source3/smbd/vfs.c | 81 | 
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;  }  | 
