diff options
-rw-r--r-- | source3/smbd/trans2.c | 393 | ||||
-rw-r--r-- | source3/smbd/vfs.c | 1 |
2 files changed, 219 insertions, 175 deletions
diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c index 98ea6c111a..826ccd3f92 100644 --- a/source3/smbd/trans2.c +++ b/source3/smbd/trans2.c @@ -4211,7 +4211,9 @@ static NTSTATUS smb_set_posix_lock(connection_struct *conn, Deal with SMB_INFO_STANDARD. ****************************************************************************/ -static NTSTATUS smb_set_info_standard(const char *pdata, int total_data, struct utimbuf *p_tvs) +static NTSTATUS smb_set_info_standard(const char *pdata, + int total_data, + struct utimbuf *p_tvs) { if (total_data < 12) { return NT_STATUS_INVALID_PARAMETER; @@ -4228,7 +4230,10 @@ static NTSTATUS smb_set_info_standard(const char *pdata, int total_data, struct Deal with SMB_SET_FILE_BASIC_INFO. ****************************************************************************/ -static NTSTATUS smb_set_file_basic_info(const char *pdata, int total_data, struct utimbuf *p_tvs, int *p_dosmode) +static NTSTATUS smb_set_file_basic_info(const char *pdata, + int total_data, + struct utimbuf *p_tvs, + int *p_dosmode) { /* Patch to do this correctly from Paul Eggert <eggert@twinsun.com>. */ time_t write_time; @@ -4273,7 +4278,6 @@ static NTSTATUS smb_set_file_allocation_info(connection_struct *conn, SMB_STRUCT_STAT *psbuf, SMB_OFF_T *p_size) { - int ret = -1; SMB_BIG_UINT allocation_size; if (total_data < 8) { @@ -4298,6 +4302,7 @@ static NTSTATUS smb_set_file_allocation_info(connection_struct *conn, } if(allocation_size != get_file_size(*psbuf)) { + int ret = -1; SMB_STRUCT_STAT new_sbuf; DEBUG(10,("smb_set_file_allocation_info: file %s : setting new allocation size to %.0f\n", @@ -4306,10 +4311,13 @@ static NTSTATUS smb_set_file_allocation_info(connection_struct *conn, if (fsp && fsp->fh->fd != -1) { /* Open file. */ ret = vfs_allocate_file_space(fsp, allocation_size); + if (ret == -1) { + return map_nt_error_from_unix(errno); + } if (SMB_VFS_FSTAT(fsp,fsp->fh->fd,&new_sbuf) != 0) { DEBUG(3,("smb_set_file_allocation_info: fstat of fnum %d failed (%s)\n", fsp->fnum, strerror(errno))); - ret = -1; + return map_nt_error_from_unix(errno); } } else { /* Pathname or stat or directory file. */ @@ -4330,16 +4338,20 @@ static NTSTATUS smb_set_file_allocation_info(connection_struct *conn, return status; } ret = vfs_allocate_file_space(new_fsp, allocation_size); + if (ret == -1) { + status = map_nt_error_from_unix(errno); + close_file(new_fsp,NORMAL_CLOSE); + return status; + } if (SMB_VFS_FSTAT(new_fsp,new_fsp->fh->fd,&new_sbuf) != 0) { DEBUG(3,("smb_set_file_allocation_info: fstat of fnum %d failed (%s)\n", new_fsp->fnum, strerror(errno))); - ret = -1; + status = map_nt_error_from_unix(errno); + close_file(new_fsp,NORMAL_CLOSE); + return status; } close_file(new_fsp,NORMAL_CLOSE); } - if (ret == -1) { - return NT_STATUS_DISK_FULL; - } /* Allocate can truncate size... */ *p_size = get_file_size(new_sbuf); @@ -4375,6 +4387,190 @@ static NTSTATUS smb_set_file_end_of_file_info(connection_struct *conn, return NT_STATUS_OK; } +/**************************************************************************** + Deal with SMB_SET_FILE_UNIX_BASIC. +****************************************************************************/ + +static NTSTATUS smb_set_file_unix_basic(connection_struct *conn, + const char *pdata, + int total_data, + files_struct *fsp, + const char *fname, + SMB_STRUCT_STAT *psbuf, + SMB_OFF_T *p_size, + struct utimbuf *p_tvs, + mode_t *p_unixmode, + int *p_dosmode) +{ + uid_t set_owner = (uid_t)SMB_UID_NO_CHANGE; + gid_t set_grp = (uid_t)SMB_GID_NO_CHANGE; + uint32 raw_unixmode; + BOOL delete_on_fail = False; + NTSTATUS status = NT_STATUS_OK; + + if (total_data < 100) { + return NT_STATUS_INVALID_PARAMETER; + } + + set_owner = VALID_STAT(*psbuf) ? psbuf->st_uid : (uid_t)SMB_UID_NO_CHANGE; + set_grp = VALID_STAT(*psbuf) ? psbuf->st_gid : (gid_t)SMB_GID_NO_CHANGE; + + if(IVAL(pdata, 0) != SMB_SIZE_NO_CHANGE_LO && + IVAL(pdata, 4) != SMB_SIZE_NO_CHANGE_HI) { + *p_size=IVAL(pdata,0); /* first 8 Bytes are size */ +#ifdef LARGE_SMB_OFF_T + *p_size |= (((SMB_OFF_T)IVAL(pdata,4)) << 32); +#else /* LARGE_SMB_OFF_T */ + if (IVAL(pdata,4) != 0) { + /* more than 32 bits? */ + return NT_STATUS_INVALID_PARAMETER; + } +#endif /* LARGE_SMB_OFF_T */ + } + + pdata+=24; /* ctime & st_blocks are not changed */ + p_tvs->actime = convert_timespec_to_time_t(interpret_long_date(pdata)); /* access_time */ + p_tvs->modtime = convert_timespec_to_time_t(interpret_long_date(pdata+8)); /* modification_time */ + pdata+=16; + set_owner = (uid_t)IVAL(pdata,0); + pdata += 8; + set_grp = (gid_t)IVAL(pdata,0); + pdata += 8; + raw_unixmode = IVAL(pdata,28); + *p_unixmode = unix_perms_from_wire(conn, psbuf, raw_unixmode); + *p_dosmode = 0; /* Ensure dos mode change doesn't override this. */ + + DEBUG(10,("smb_set_file_unix_basic: SMB_SET_FILE_UNIX_BASIC: name = %s \ +size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n", + fname, (double)*p_size, (unsigned int)set_owner, (unsigned int)set_grp, (int)raw_unixmode)); + + if (!VALID_STAT(*psbuf)) { + + /* + * The only valid use of this is to create character and block + * devices, and named pipes. This is deprecated (IMHO) and + * a new info level should be used for mknod. JRA. + */ + + uint32 file_type = IVAL(pdata,0); +#if defined(HAVE_MAKEDEV) + uint32 dev_major = IVAL(pdata,4); + uint32 dev_minor = IVAL(pdata,12); +#endif + + SMB_DEV_T dev = (SMB_DEV_T)0; + + if (raw_unixmode == SMB_MODE_NO_CHANGE) { + return NT_STATUS_INVALID_PARAMETER; + } + +#if defined(HAVE_MAKEDEV) + dev = makedev(dev_major, dev_minor); +#endif + + switch (file_type) { +#if defined(S_IFIFO) + case UNIX_TYPE_FIFO: + *p_unixmode |= S_IFIFO; + break; +#endif +#if defined(S_IFSOCK) + case UNIX_TYPE_SOCKET: + *p_unixmode |= S_IFSOCK; + break; +#endif +#if defined(S_IFCHR) + case UNIX_TYPE_CHARDEV: + *p_unixmode |= S_IFCHR; + break; +#endif +#if defined(S_IFBLK) + case UNIX_TYPE_BLKDEV: + *p_unixmode |= S_IFBLK; + break; +#endif + default: + return NT_STATUS_INVALID_PARAMETER; + } + + DEBUG(10,("smb_set_file_unix_basic: SMB_SET_FILE_UNIX_BASIC doing mknod dev %.0f mode \ +0%o for file %s\n", (double)dev, *p_unixmode, fname )); + + /* Ok - do the mknod. */ + if (SMB_VFS_MKNOD(conn, fname, *p_unixmode, dev) != 0) { + return map_nt_error_from_unix(errno); + } + + /* If any of the other "set" calls fail we + * don't want to end up with a half-constructed mknod. + */ + + delete_on_fail = True; + + if (lp_inherit_perms(SNUM(conn))) { + inherit_access_acl( + conn, parent_dirname(fname), + fname, *p_unixmode); + } + + if (SMB_VFS_STAT(conn, fname, psbuf) != 0) { + status = map_nt_error_from_unix(errno); + SMB_VFS_UNLINK(conn,fname); + return status; + } + + /* Ensure we don't try and change anything else. */ + raw_unixmode = SMB_MODE_NO_CHANGE; + *p_size = get_file_size(*psbuf); + p_tvs->modtime = psbuf->st_mtime; + p_tvs->actime = psbuf->st_atime; + } + + /* + * Deal with the UNIX specific mode set. + */ + + if (raw_unixmode != SMB_MODE_NO_CHANGE) { + DEBUG(10,("smb_set_file_unix_basic: SMB_SET_FILE_UNIX_BASIC setting mode 0%o for file %s\n", + (unsigned int)*p_unixmode, fname )); + if (SMB_VFS_CHMOD(conn,fname,*p_unixmode) != 0) { + return map_nt_error_from_unix(errno); + } + } + + /* + * Deal with the UNIX specific uid set. + */ + + if ((set_owner != (uid_t)SMB_UID_NO_CHANGE) && (psbuf->st_uid != set_owner)) { + DEBUG(10,("smb_set_file_unix_basic: SMB_SET_FILE_UNIX_BASIC changing owner %u for file %s\n", + (unsigned int)set_owner, fname )); + if (SMB_VFS_CHOWN(conn, fname, set_owner, (gid_t)-1) != 0) { + status = map_nt_error_from_unix(errno); + if (delete_on_fail) { + SMB_VFS_UNLINK(conn,fname); + } + return status; + } + } + + /* + * Deal with the UNIX specific gid set. + */ + + if ((set_grp != (uid_t)SMB_GID_NO_CHANGE) && (psbuf->st_gid != set_grp)) { + DEBUG(10,("smb_set_file_unix_basic: SMB_SET_FILE_UNIX_BASIC changing group %u for file %s\n", + (unsigned int)set_owner, fname )); + if (SMB_VFS_CHOWN(conn, fname, (uid_t)-1, set_grp) != 0) { + status = map_nt_error_from_unix(errno); + if (delete_on_fail) { + SMB_VFS_UNLINK(conn,fname); + } + return status; + } + } + return NT_STATUS_OK; +} /**************************************************************************** Reply to a TRANS2_SETFILEINFO (set file info by fileid or pathname). @@ -4395,8 +4591,6 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char pstring fname; int fd = -1; files_struct *fsp = NULL; - uid_t set_owner = (uid_t)SMB_UID_NO_CHANGE; - gid_t set_grp = (uid_t)SMB_GID_NO_CHANGE; mode_t unixmode = 0; NTSTATUS status = NT_STATUS_OK; @@ -4520,9 +4714,6 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char dosmode = dos_mode(conn,fname,&sbuf); unixmode = sbuf.st_mode; - set_owner = VALID_STAT(sbuf) ? sbuf.st_uid : (uid_t)SMB_UID_NO_CHANGE; - set_grp = VALID_STAT(sbuf) ? sbuf.st_gid : (gid_t)SMB_GID_NO_CHANGE; - switch (info_level) { case SMB_INFO_STANDARD: @@ -4654,170 +4845,22 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char case SMB_SET_FILE_UNIX_BASIC: { - uint32 raw_unixmode; - BOOL delete_on_fail = False; - - if (total_data < 100) { - return ERROR_NT(NT_STATUS_INVALID_PARAMETER); - } - - if(IVAL(pdata, 0) != SMB_SIZE_NO_CHANGE_LO && - IVAL(pdata, 4) != SMB_SIZE_NO_CHANGE_HI) { - size=IVAL(pdata,0); /* first 8 Bytes are size */ -#ifdef LARGE_SMB_OFF_T - size |= (((SMB_OFF_T)IVAL(pdata,4)) << 32); -#else /* LARGE_SMB_OFF_T */ - if (IVAL(pdata,4) != 0) { - /* more than 32 bits? */ - return ERROR_NT(NT_STATUS_INVALID_PARAMETER); - } -#endif /* LARGE_SMB_OFF_T */ - } - pdata+=24; /* ctime & st_blocks are not changed */ - tvs.actime = convert_timespec_to_time_t(interpret_long_date(pdata)); /* access_time */ - tvs.modtime = convert_timespec_to_time_t(interpret_long_date(pdata+8)); /* modification_time */ - pdata+=16; - set_owner = (uid_t)IVAL(pdata,0); - pdata += 8; - set_grp = (gid_t)IVAL(pdata,0); - pdata += 8; - raw_unixmode = IVAL(pdata,28); - unixmode = unix_perms_from_wire(conn, &sbuf, raw_unixmode); - dosmode = 0; /* Ensure dos mode change doesn't override this. */ - - DEBUG(10,("call_trans2setfilepathinfo: SMB_SET_FILE_UNIX_BASIC: name = %s \ -size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n", - fname, (double)size, (unsigned int)set_owner, (unsigned int)set_grp, (int)raw_unixmode)); - - if (!VALID_STAT(sbuf)) { - - /* - * The only valid use of this is to create character and block - * devices, and named pipes. This is deprecated (IMHO) and - * a new info level should be used for mknod. JRA. - */ - - uint32 file_type = IVAL(pdata,0); -#if defined(HAVE_MAKEDEV) - uint32 dev_major = IVAL(pdata,4); - uint32 dev_minor = IVAL(pdata,12); -#endif - - SMB_DEV_T dev = (SMB_DEV_T)0; - - if (tran_call == TRANSACT2_SETFILEINFO) - return(ERROR_DOS(ERRDOS,ERRnoaccess)); - - if (raw_unixmode == SMB_MODE_NO_CHANGE) { - return ERROR_NT(NT_STATUS_INVALID_PARAMETER); - } - -#if defined(HAVE_MAKEDEV) - dev = makedev(dev_major, dev_minor); -#endif - - switch (file_type) { -#if defined(S_IFIFO) - case UNIX_TYPE_FIFO: - unixmode |= S_IFIFO; - break; -#endif -#if defined(S_IFSOCK) - case UNIX_TYPE_SOCKET: - unixmode |= S_IFSOCK; - break; -#endif -#if defined(S_IFCHR) - case UNIX_TYPE_CHARDEV: - unixmode |= S_IFCHR; - break; -#endif -#if defined(S_IFBLK) - case UNIX_TYPE_BLKDEV: - unixmode |= S_IFBLK; - break; -#endif - default: - return(ERROR_DOS(ERRDOS,ERRnoaccess)); - } - - DEBUG(10,("call_trans2setfilepathinfo: SMB_SET_FILE_UNIX_BASIC doing mknod dev %.0f mode \ -0%o for file %s\n", (double)dev, unixmode, fname )); - - /* Ok - do the mknod. */ - if (SMB_VFS_MKNOD(conn,fname, unixmode, dev) != 0) { - return(UNIXERROR(ERRDOS,ERRnoaccess)); - } - - /* If any of the other "set" calls fail we - * don't want to end up with a half-constructed mknod. - */ - - delete_on_fail = True; - - if (lp_inherit_perms(SNUM(conn))) { - inherit_access_acl( - conn, parent_dirname(fname), - fname, unixmode); - } - - if (SMB_VFS_STAT(conn, fname, &sbuf) != 0) { - int saved_errno = errno; - SMB_VFS_UNLINK(conn,fname); - errno = saved_errno; - return(UNIXERROR(ERRDOS,ERRnoaccess)); - } - - /* Ensure we don't try and change anything else. */ - raw_unixmode = SMB_MODE_NO_CHANGE; - size = get_file_size(sbuf); - tvs.modtime = sbuf.st_mtime; - tvs.actime = sbuf.st_atime; - } - - /* - * Deal with the UNIX specific mode set. - */ - - if (raw_unixmode != SMB_MODE_NO_CHANGE) { - DEBUG(10,("call_trans2setfilepathinfo: SMB_SET_FILE_UNIX_BASIC setting mode 0%o for file %s\n", - (unsigned int)unixmode, fname )); - if (SMB_VFS_CHMOD(conn,fname,unixmode) != 0) - return(UNIXERROR(ERRDOS,ERRnoaccess)); - } - - /* - * Deal with the UNIX specific uid set. - */ - - if ((set_owner != (uid_t)SMB_UID_NO_CHANGE) && (sbuf.st_uid != set_owner)) { - DEBUG(10,("call_trans2setfilepathinfo: SMB_SET_FILE_UNIX_BASIC changing owner %u for file %s\n", - (unsigned int)set_owner, fname )); - if (SMB_VFS_CHOWN(conn,fname,set_owner, (gid_t)-1) != 0) { - if (delete_on_fail) { - int saved_errno = errno; - SMB_VFS_UNLINK(conn,fname); - errno = saved_errno; - } - return(UNIXERROR(ERRDOS,ERRnoaccess)); - } + if (tran_call == TRANSACT2_SETFILEINFO) { + return ERROR_NT(NT_STATUS_INVALID_LEVEL); } - /* - * Deal with the UNIX specific gid set. - */ - - if ((set_grp != (uid_t)SMB_GID_NO_CHANGE) && (sbuf.st_gid != set_grp)) { - DEBUG(10,("call_trans2setfilepathinfo: SMB_SET_FILE_UNIX_BASIC changing group %u for file %s\n", - (unsigned int)set_owner, fname )); - if (SMB_VFS_CHOWN(conn,fname,(uid_t)-1, set_grp) != 0) { - if (delete_on_fail) { - int saved_errno = errno; - SMB_VFS_UNLINK(conn,fname); - errno = saved_errno; - } - return(UNIXERROR(ERRDOS,ERRnoaccess)); - } + status = smb_set_file_unix_basic(conn, + pdata, + total_data, + fsp, + fname, + &sbuf, + &size, + &tvs, + &unixmode, + &dosmode); + if (!NT_STATUS_IS_OK(status)) { + return ERROR_NT(status); } break; } diff --git a/source3/smbd/vfs.c b/source3/smbd/vfs.c index 47ac9ef461..643c48cd27 100644 --- a/source3/smbd/vfs.c +++ b/source3/smbd/vfs.c @@ -454,6 +454,7 @@ int vfs_allocate_file_space(files_struct *fsp, SMB_BIG_UINT len) if (((SMB_OFF_T)len) < 0) { DEBUG(0,("vfs_allocate_file_space: %s negative len requested.\n", fsp->fsp_name )); + errno = EINVAL; return -1; } |