diff options
Diffstat (limited to 'source3/smbd')
-rw-r--r-- | source3/smbd/trans2.c | 54 | ||||
-rw-r--r-- | source3/smbd/vfs.c | 81 |
2 files changed, 123 insertions, 12 deletions
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; } |