mirror of
https://github.com/samba-team/samba.git
synced 2024-12-27 03:21:53 +03:00
4f69b76fa1
Signed-off-by: Volker Lendecke <vl@samba.org> Reviewed-by: Ralph Boehme <slow@samba.org>
5725 lines
145 KiB
C
5725 lines
145 KiB
C
/*
|
|
Unix SMB/CIFS implementation.
|
|
SMB transaction2 handling
|
|
Copyright (C) Jeremy Allison 1994-2007
|
|
Copyright (C) Stefan (metze) Metzmacher 2003
|
|
Copyright (C) Volker Lendecke 2005-2007
|
|
Copyright (C) Steve French 2005
|
|
Copyright (C) James Peach 2006-2007
|
|
|
|
Extensively modified by Andrew Tridgell, 1995
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "includes.h"
|
|
#include "ntioctl.h"
|
|
#include "system/filesys.h"
|
|
#include "lib/util/time_basic.h"
|
|
#include "version.h"
|
|
#include "smbd/smbd.h"
|
|
#include "smbd/globals.h"
|
|
#include "../libcli/auth/libcli_auth.h"
|
|
#include "../librpc/gen_ndr/xattr.h"
|
|
#include "../librpc/gen_ndr/ndr_security.h"
|
|
#include "libcli/security/security.h"
|
|
#include "trans2.h"
|
|
#include "auth.h"
|
|
#include "smbprofile.h"
|
|
#include "rpc_server/srv_pipe_hnd.h"
|
|
#include "printing.h"
|
|
#include "lib/util_ea.h"
|
|
#include "lib/readdir_attr.h"
|
|
#include "messages.h"
|
|
#include "libcli/smb/smb2_posix.h"
|
|
#include "lib/util/string_wrappers.h"
|
|
#include "source3/lib/substitute.h"
|
|
#include "source3/lib/adouble.h"
|
|
|
|
#define DIR_ENTRY_SAFETY_MARGIN 4096
|
|
|
|
static uint32_t generate_volume_serial_number(
|
|
const struct loadparm_substitution *lp_sub,
|
|
int snum);
|
|
|
|
/****************************************************************************
|
|
Check if an open file handle is a symlink.
|
|
****************************************************************************/
|
|
|
|
NTSTATUS refuse_symlink_fsp(const files_struct *fsp)
|
|
{
|
|
|
|
if (!VALID_STAT(fsp->fsp_name->st)) {
|
|
return NT_STATUS_ACCESS_DENIED;
|
|
}
|
|
if (S_ISLNK(fsp->fsp_name->st.st_ex_mode)) {
|
|
return NT_STATUS_ACCESS_DENIED;
|
|
}
|
|
if (fsp_get_pathref_fd(fsp) == -1) {
|
|
return NT_STATUS_ACCESS_DENIED;
|
|
}
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
NTSTATUS check_access_fsp(struct files_struct *fsp,
|
|
uint32_t access_mask)
|
|
{
|
|
if (!fsp->fsp_flags.is_fsa) {
|
|
return smbd_check_access_rights_fsp(fsp->conn->cwd_fsp,
|
|
fsp,
|
|
false,
|
|
access_mask);
|
|
}
|
|
if (!(fsp->access_mask & access_mask)) {
|
|
return NT_STATUS_ACCESS_DENIED;
|
|
}
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
#if defined(HAVE_POSIX_ACLS)
|
|
/****************************************************************************
|
|
Utility function to open a fsp for a POSIX handle operation.
|
|
****************************************************************************/
|
|
|
|
NTSTATUS get_posix_fsp(connection_struct *conn,
|
|
struct smb_request *req,
|
|
struct smb_filename *smb_fname,
|
|
uint32_t access_mask,
|
|
files_struct **ret_fsp)
|
|
{
|
|
NTSTATUS status;
|
|
uint32_t create_disposition = FILE_OPEN;
|
|
uint32_t share_access = FILE_SHARE_READ|
|
|
FILE_SHARE_WRITE|
|
|
FILE_SHARE_DELETE;
|
|
struct smb2_create_blobs *posx = NULL;
|
|
|
|
/*
|
|
* Only FILE_FLAG_POSIX_SEMANTICS matters on existing files,
|
|
* but set reasonable defaults.
|
|
*/
|
|
uint32_t file_attributes = 0664;
|
|
uint32_t oplock = NO_OPLOCK;
|
|
uint32_t create_options = FILE_NON_DIRECTORY_FILE;
|
|
|
|
/* File or directory must exist. */
|
|
if (!VALID_STAT(smb_fname->st)) {
|
|
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
/* Cannot be a symlink. */
|
|
if (S_ISLNK(smb_fname->st.st_ex_mode)) {
|
|
return NT_STATUS_ACCESS_DENIED;
|
|
}
|
|
/* Set options correctly for directory open. */
|
|
if (S_ISDIR(smb_fname->st.st_ex_mode)) {
|
|
/*
|
|
* Only FILE_FLAG_POSIX_SEMANTICS matters on existing
|
|
* directories, but set reasonable defaults.
|
|
*/
|
|
file_attributes = 0775;
|
|
create_options = FILE_DIRECTORY_FILE;
|
|
}
|
|
|
|
status = make_smb2_posix_create_ctx(
|
|
talloc_tos(), &posx, file_attributes);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
DBG_WARNING("make_smb2_posix_create_ctx failed: %s\n",
|
|
nt_errstr(status));
|
|
goto done;
|
|
}
|
|
|
|
status = SMB_VFS_CREATE_FILE(
|
|
conn, /* conn */
|
|
req, /* req */
|
|
NULL, /* dirfsp */
|
|
smb_fname, /* fname */
|
|
access_mask, /* access_mask */
|
|
share_access, /* share_access */
|
|
create_disposition,/* create_disposition*/
|
|
create_options, /* create_options */
|
|
file_attributes,/* file_attributes */
|
|
oplock, /* oplock_request */
|
|
NULL, /* lease */
|
|
0, /* allocation_size */
|
|
0, /* private_flags */
|
|
NULL, /* sd */
|
|
NULL, /* ea_list */
|
|
ret_fsp, /* result */
|
|
NULL, /* pinfo */
|
|
posx, /* in_context */
|
|
NULL); /* out_context */
|
|
|
|
done:
|
|
TALLOC_FREE(posx);
|
|
return status;
|
|
}
|
|
#endif
|
|
|
|
/********************************************************************
|
|
Roundup a value to the nearest allocation roundup size boundary.
|
|
Only do this for Windows clients.
|
|
********************************************************************/
|
|
|
|
uint64_t smb_roundup(connection_struct *conn, uint64_t val)
|
|
{
|
|
uint64_t rval = lp_allocation_roundup_size(SNUM(conn));
|
|
|
|
/* Only roundup for Windows clients. */
|
|
enum remote_arch_types ra_type = get_remote_arch();
|
|
if (rval && (ra_type != RA_SAMBA) && (ra_type != RA_CIFSFS)) {
|
|
val = SMB_ROUNDUP(val,rval);
|
|
}
|
|
return val;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Utility functions for dealing with extended attributes.
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
Refuse to allow clients to overwrite our private xattrs.
|
|
****************************************************************************/
|
|
|
|
bool samba_private_attr_name(const char *unix_ea_name)
|
|
{
|
|
static const char * const prohibited_ea_names[] = {
|
|
SAMBA_POSIX_INHERITANCE_EA_NAME,
|
|
SAMBA_XATTR_DOS_ATTRIB,
|
|
SAMBA_XATTR_MARKER,
|
|
XATTR_NTACL_NAME,
|
|
AFPINFO_EA_NETATALK,
|
|
NULL
|
|
};
|
|
|
|
int i;
|
|
|
|
for (i = 0; prohibited_ea_names[i]; i++) {
|
|
if (strequal( prohibited_ea_names[i], unix_ea_name))
|
|
return true;
|
|
}
|
|
if (strncasecmp_m(unix_ea_name, SAMBA_XATTR_DOSSTREAM_PREFIX,
|
|
strlen(SAMBA_XATTR_DOSSTREAM_PREFIX)) == 0) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Get one EA value. Fill in a struct ea_struct.
|
|
****************************************************************************/
|
|
|
|
NTSTATUS get_ea_value_fsp(TALLOC_CTX *mem_ctx,
|
|
files_struct *fsp,
|
|
const char *ea_name,
|
|
struct ea_struct *pea)
|
|
{
|
|
/* Get the value of this xattr. Max size is 64k. */
|
|
size_t attr_size = 256;
|
|
char *val = NULL;
|
|
ssize_t sizeret;
|
|
size_t max_xattr_size = 0;
|
|
|
|
if (fsp == NULL) {
|
|
return NT_STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
max_xattr_size = lp_smbd_max_xattr_size(SNUM(fsp->conn));
|
|
|
|
again:
|
|
|
|
val = talloc_realloc(mem_ctx, val, char, attr_size);
|
|
if (!val) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
sizeret = SMB_VFS_FGETXATTR(fsp, ea_name, val, attr_size);
|
|
if (sizeret == -1 && errno == ERANGE && attr_size < max_xattr_size) {
|
|
attr_size = max_xattr_size;
|
|
goto again;
|
|
}
|
|
|
|
if (sizeret == -1) {
|
|
return map_nt_error_from_unix(errno);
|
|
}
|
|
|
|
DEBUG(10,("get_ea_value: EA %s is of length %u\n", ea_name, (unsigned int)sizeret));
|
|
dump_data(10, (uint8_t *)val, sizeret);
|
|
|
|
pea->flags = 0;
|
|
if (strnequal(ea_name, "user.", 5)) {
|
|
pea->name = talloc_strdup(mem_ctx, &ea_name[5]);
|
|
} else {
|
|
pea->name = talloc_strdup(mem_ctx, ea_name);
|
|
}
|
|
if (pea->name == NULL) {
|
|
TALLOC_FREE(val);
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
pea->value.data = (unsigned char *)val;
|
|
pea->value.length = (size_t)sizeret;
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
NTSTATUS get_ea_names_from_fsp(TALLOC_CTX *mem_ctx,
|
|
files_struct *fsp,
|
|
char ***pnames,
|
|
size_t *pnum_names)
|
|
{
|
|
char smallbuf[1024];
|
|
/* Get a list of all xattrs. Max namesize is 64k. */
|
|
size_t ea_namelist_size = 1024;
|
|
char *ea_namelist = smallbuf;
|
|
char *to_free = NULL;
|
|
|
|
char *p;
|
|
char **names;
|
|
size_t num_names;
|
|
ssize_t sizeret = -1;
|
|
NTSTATUS status;
|
|
|
|
if (pnames) {
|
|
*pnames = NULL;
|
|
}
|
|
*pnum_names = 0;
|
|
|
|
if (fsp == NULL) {
|
|
/*
|
|
* Callers may pass fsp == NULL when passing smb_fname->fsp of a
|
|
* symlink. This is ok, handle it here, by just return no EA's
|
|
* on a symlink.
|
|
*/
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
/* should be the case that fsp != NULL */
|
|
SMB_ASSERT(fsp != NULL);
|
|
|
|
sizeret = SMB_VFS_FLISTXATTR(fsp, ea_namelist,
|
|
ea_namelist_size);
|
|
|
|
if ((sizeret == -1) && (errno == ERANGE)) {
|
|
ea_namelist_size = 65536;
|
|
ea_namelist = talloc_array(mem_ctx, char, ea_namelist_size);
|
|
if (ea_namelist == NULL) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
to_free = ea_namelist;
|
|
|
|
sizeret = SMB_VFS_FLISTXATTR(fsp, ea_namelist,
|
|
ea_namelist_size);
|
|
}
|
|
|
|
if (sizeret == -1) {
|
|
status = map_nt_error_from_unix(errno);
|
|
TALLOC_FREE(to_free);
|
|
return status;
|
|
}
|
|
|
|
DBG_DEBUG("ea_namelist size = %zd\n", sizeret);
|
|
|
|
if (sizeret == 0) {
|
|
TALLOC_FREE(to_free);
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
/*
|
|
* Ensure the result is 0-terminated
|
|
*/
|
|
|
|
if (ea_namelist[sizeret-1] != '\0') {
|
|
TALLOC_FREE(to_free);
|
|
return NT_STATUS_INTERNAL_ERROR;
|
|
}
|
|
|
|
/*
|
|
* count the names
|
|
*/
|
|
num_names = 0;
|
|
|
|
for (p = ea_namelist; p - ea_namelist < sizeret; p += strlen(p)+1) {
|
|
num_names += 1;
|
|
}
|
|
|
|
*pnum_names = num_names;
|
|
|
|
if (pnames == NULL) {
|
|
TALLOC_FREE(to_free);
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
names = talloc_array(mem_ctx, char *, num_names);
|
|
if (names == NULL) {
|
|
DEBUG(0, ("talloc failed\n"));
|
|
TALLOC_FREE(to_free);
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
if (ea_namelist == smallbuf) {
|
|
ea_namelist = talloc_memdup(names, smallbuf, sizeret);
|
|
if (ea_namelist == NULL) {
|
|
TALLOC_FREE(names);
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
} else {
|
|
talloc_steal(names, ea_namelist);
|
|
|
|
ea_namelist = talloc_realloc(names, ea_namelist, char,
|
|
sizeret);
|
|
if (ea_namelist == NULL) {
|
|
TALLOC_FREE(names);
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
}
|
|
|
|
num_names = 0;
|
|
|
|
for (p = ea_namelist; p - ea_namelist < sizeret; p += strlen(p)+1) {
|
|
names[num_names++] = p;
|
|
}
|
|
|
|
*pnames = names;
|
|
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Return a linked list of the total EA's. Plus the total size
|
|
****************************************************************************/
|
|
|
|
static NTSTATUS get_ea_list_from_fsp(TALLOC_CTX *mem_ctx,
|
|
files_struct *fsp,
|
|
size_t *pea_total_len,
|
|
struct ea_list **ea_list)
|
|
{
|
|
/* Get a list of all xattrs. Max namesize is 64k. */
|
|
size_t i, num_names;
|
|
char **names;
|
|
struct ea_list *ea_list_head = NULL;
|
|
bool posix_pathnames = false;
|
|
NTSTATUS status;
|
|
|
|
*pea_total_len = 0;
|
|
*ea_list = NULL;
|
|
|
|
/* symlink */
|
|
if (fsp == NULL) {
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
if (!lp_ea_support(SNUM(fsp->conn))) {
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
if (fsp_is_alternate_stream(fsp)) {
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
posix_pathnames = (fsp->fsp_name->flags & SMB_FILENAME_POSIX_PATH);
|
|
|
|
status = get_ea_names_from_fsp(talloc_tos(),
|
|
fsp,
|
|
&names,
|
|
&num_names);
|
|
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
|
|
if (num_names == 0) {
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
for (i=0; i<num_names; i++) {
|
|
struct ea_list *listp;
|
|
fstring dos_ea_name;
|
|
|
|
/*
|
|
* POSIX EA names are divided into several namespaces by
|
|
* means of string prefixes. Usually, the system controls
|
|
* semantics for each namespace, but the 'user' namespace is
|
|
* available for arbitrary use, which comes closest to
|
|
* Windows EA semantics. Hence, we map POSIX EAs from the
|
|
* 'user' namespace to Windows EAs, and just ignore all the
|
|
* other namespaces. Also, a few specific names in the 'user'
|
|
* namespace are used by Samba internally. Filter them out as
|
|
* well, and only present the EAs that are available for
|
|
* arbitrary use.
|
|
*/
|
|
if (!strnequal(names[i], "user.", 5)
|
|
|| samba_private_attr_name(names[i]))
|
|
continue;
|
|
|
|
/*
|
|
* Filter out any underlying POSIX EA names
|
|
* that a Windows client can't handle.
|
|
*/
|
|
if (!posix_pathnames &&
|
|
is_invalid_windows_ea_name(names[i])) {
|
|
continue;
|
|
}
|
|
|
|
listp = talloc(mem_ctx, struct ea_list);
|
|
if (listp == NULL) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
status = get_ea_value_fsp(listp,
|
|
fsp,
|
|
names[i],
|
|
&listp->ea);
|
|
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
TALLOC_FREE(listp);
|
|
return status;
|
|
}
|
|
|
|
if (listp->ea.value.length == 0) {
|
|
/*
|
|
* We can never return a zero length EA.
|
|
* Windows reports the EA's as corrupted.
|
|
*/
|
|
TALLOC_FREE(listp);
|
|
continue;
|
|
} else if (listp->ea.value.length > 65536) {
|
|
/*
|
|
* SMB clients may report error with file
|
|
* if large EA is presented to them.
|
|
*/
|
|
DBG_ERR("EA [%s] on file [%s] exceeds "
|
|
"maximum permitted EA size of 64KiB: %zu\n.",
|
|
listp->ea.name, fsp_str_dbg(fsp),
|
|
listp->ea.value.length);
|
|
TALLOC_FREE(listp);
|
|
continue;
|
|
}
|
|
|
|
push_ascii_fstring(dos_ea_name, listp->ea.name);
|
|
|
|
*pea_total_len +=
|
|
4 + strlen(dos_ea_name) + 1 + listp->ea.value.length;
|
|
|
|
DEBUG(10,("get_ea_list_from_file: total_len = %u, %s, val len "
|
|
"= %u\n", (unsigned int)*pea_total_len, dos_ea_name,
|
|
(unsigned int)listp->ea.value.length));
|
|
|
|
DLIST_ADD_END(ea_list_head, listp);
|
|
|
|
}
|
|
|
|
/* Add on 4 for total length. */
|
|
if (*pea_total_len) {
|
|
*pea_total_len += 4;
|
|
}
|
|
|
|
DEBUG(10, ("get_ea_list_from_file: total_len = %u\n",
|
|
(unsigned int)*pea_total_len));
|
|
|
|
*ea_list = ea_list_head;
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Fill a qfilepathinfo buffer with EA's. Returns the length of the buffer
|
|
that was filled.
|
|
****************************************************************************/
|
|
|
|
static unsigned int fill_ea_buffer(TALLOC_CTX *mem_ctx, char *pdata, unsigned int total_data_size,
|
|
connection_struct *conn, struct ea_list *ea_list)
|
|
{
|
|
unsigned int ret_data_size = 4;
|
|
char *p = pdata;
|
|
|
|
SMB_ASSERT(total_data_size >= 4);
|
|
|
|
if (!lp_ea_support(SNUM(conn))) {
|
|
SIVAL(pdata,4,0);
|
|
return 4;
|
|
}
|
|
|
|
for (p = pdata + 4; ea_list; ea_list = ea_list->next) {
|
|
size_t dos_namelen;
|
|
fstring dos_ea_name;
|
|
push_ascii_fstring(dos_ea_name, ea_list->ea.name);
|
|
dos_namelen = strlen(dos_ea_name);
|
|
if (dos_namelen > 255 || dos_namelen == 0) {
|
|
break;
|
|
}
|
|
if (ea_list->ea.value.length > 65535) {
|
|
break;
|
|
}
|
|
if (4 + dos_namelen + 1 + ea_list->ea.value.length > total_data_size) {
|
|
break;
|
|
}
|
|
|
|
/* We know we have room. */
|
|
SCVAL(p,0,ea_list->ea.flags);
|
|
SCVAL(p,1,dos_namelen);
|
|
SSVAL(p,2,ea_list->ea.value.length);
|
|
strlcpy(p+4, dos_ea_name, dos_namelen+1);
|
|
if (ea_list->ea.value.length > 0) {
|
|
memcpy(p + 4 + dos_namelen + 1,
|
|
ea_list->ea.value.data,
|
|
ea_list->ea.value.length);
|
|
}
|
|
|
|
total_data_size -= 4 + dos_namelen + 1 + ea_list->ea.value.length;
|
|
p += 4 + dos_namelen + 1 + ea_list->ea.value.length;
|
|
}
|
|
|
|
ret_data_size = PTR_DIFF(p, pdata);
|
|
DEBUG(10,("fill_ea_buffer: data_size = %u\n", ret_data_size ));
|
|
SIVAL(pdata,0,ret_data_size);
|
|
return ret_data_size;
|
|
}
|
|
|
|
static NTSTATUS fill_ea_chained_buffer(TALLOC_CTX *mem_ctx,
|
|
char *pdata,
|
|
unsigned int total_data_size,
|
|
unsigned int *ret_data_size,
|
|
connection_struct *conn,
|
|
struct ea_list *ea_list)
|
|
{
|
|
uint8_t *p = (uint8_t *)pdata;
|
|
uint8_t *last_start = NULL;
|
|
bool do_store_data = (pdata != NULL);
|
|
|
|
*ret_data_size = 0;
|
|
|
|
if (!lp_ea_support(SNUM(conn))) {
|
|
return NT_STATUS_NO_EAS_ON_FILE;
|
|
}
|
|
|
|
for (; ea_list; ea_list = ea_list->next) {
|
|
size_t dos_namelen;
|
|
fstring dos_ea_name;
|
|
size_t this_size;
|
|
size_t pad = 0;
|
|
|
|
if (last_start != NULL && do_store_data) {
|
|
SIVAL(last_start, 0, PTR_DIFF(p, last_start));
|
|
}
|
|
last_start = p;
|
|
|
|
push_ascii_fstring(dos_ea_name, ea_list->ea.name);
|
|
dos_namelen = strlen(dos_ea_name);
|
|
if (dos_namelen > 255 || dos_namelen == 0) {
|
|
return NT_STATUS_INTERNAL_ERROR;
|
|
}
|
|
if (ea_list->ea.value.length > 65535) {
|
|
return NT_STATUS_INTERNAL_ERROR;
|
|
}
|
|
|
|
this_size = 0x08 + dos_namelen + 1 + ea_list->ea.value.length;
|
|
|
|
if (ea_list->next) {
|
|
pad = (4 - (this_size % 4)) % 4;
|
|
this_size += pad;
|
|
}
|
|
|
|
if (do_store_data) {
|
|
if (this_size > total_data_size) {
|
|
return NT_STATUS_INFO_LENGTH_MISMATCH;
|
|
}
|
|
|
|
/* We know we have room. */
|
|
SIVAL(p, 0x00, 0); /* next offset */
|
|
SCVAL(p, 0x04, ea_list->ea.flags);
|
|
SCVAL(p, 0x05, dos_namelen);
|
|
SSVAL(p, 0x06, ea_list->ea.value.length);
|
|
strlcpy((char *)(p+0x08), dos_ea_name, dos_namelen+1);
|
|
memcpy(p + 0x08 + dos_namelen + 1, ea_list->ea.value.data, ea_list->ea.value.length);
|
|
if (pad) {
|
|
memset(p + 0x08 + dos_namelen + 1 + ea_list->ea.value.length,
|
|
'\0',
|
|
pad);
|
|
}
|
|
total_data_size -= this_size;
|
|
}
|
|
|
|
p += this_size;
|
|
}
|
|
|
|
*ret_data_size = PTR_DIFF(p, pdata);
|
|
DEBUG(10,("fill_ea_chained_buffer: data_size = %u\n", *ret_data_size));
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
unsigned int estimate_ea_size(files_struct *fsp)
|
|
{
|
|
size_t total_ea_len = 0;
|
|
TALLOC_CTX *mem_ctx;
|
|
struct ea_list *ea_list = NULL;
|
|
NTSTATUS status;
|
|
|
|
/* symlink */
|
|
if (fsp == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
if (!lp_ea_support(SNUM(fsp->conn))) {
|
|
return 0;
|
|
}
|
|
|
|
mem_ctx = talloc_stackframe();
|
|
|
|
/* If this is a stream fsp, then we need to instead find the
|
|
* estimated ea len from the main file, not the stream
|
|
* (streams cannot have EAs), but the estimate isn't just 0 in
|
|
* this case! */
|
|
fsp = metadata_fsp(fsp);
|
|
(void)get_ea_list_from_fsp(mem_ctx,
|
|
fsp,
|
|
&total_ea_len,
|
|
&ea_list);
|
|
|
|
if(fsp->conn->sconn->using_smb2) {
|
|
unsigned int ret_data_size;
|
|
/*
|
|
* We're going to be using fill_ea_chained_buffer() to
|
|
* marshall EA's - this size is significantly larger
|
|
* than the SMB1 buffer. Re-calculate the size without
|
|
* marshalling.
|
|
*/
|
|
status = fill_ea_chained_buffer(mem_ctx,
|
|
NULL,
|
|
0,
|
|
&ret_data_size,
|
|
fsp->conn,
|
|
ea_list);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
ret_data_size = 0;
|
|
}
|
|
total_ea_len = ret_data_size;
|
|
}
|
|
TALLOC_FREE(mem_ctx);
|
|
return total_ea_len;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Ensure the EA name is case insensitive by matching any existing EA name.
|
|
****************************************************************************/
|
|
|
|
static void canonicalize_ea_name(files_struct *fsp,
|
|
fstring unix_ea_name)
|
|
{
|
|
size_t total_ea_len;
|
|
TALLOC_CTX *mem_ctx = talloc_tos();
|
|
struct ea_list *ea_list;
|
|
NTSTATUS status = get_ea_list_from_fsp(mem_ctx,
|
|
fsp,
|
|
&total_ea_len,
|
|
&ea_list);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return;
|
|
}
|
|
|
|
for (; ea_list; ea_list = ea_list->next) {
|
|
if (strequal(&unix_ea_name[5], ea_list->ea.name)) {
|
|
DEBUG(10,("canonicalize_ea_name: %s -> %s\n",
|
|
&unix_ea_name[5], ea_list->ea.name));
|
|
strlcpy(&unix_ea_name[5], ea_list->ea.name, sizeof(fstring)-5);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Set or delete an extended attribute.
|
|
****************************************************************************/
|
|
|
|
NTSTATUS set_ea(connection_struct *conn, files_struct *fsp,
|
|
struct ea_list *ea_list)
|
|
{
|
|
NTSTATUS status;
|
|
bool posix_pathnames = false;
|
|
|
|
if (!lp_ea_support(SNUM(conn))) {
|
|
return NT_STATUS_EAS_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (fsp == NULL) {
|
|
return NT_STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
posix_pathnames = (fsp->fsp_name->flags & SMB_FILENAME_POSIX_PATH);
|
|
|
|
status = refuse_symlink_fsp(fsp);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
|
|
status = check_access_fsp(fsp, FILE_WRITE_EA);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
|
|
/* Setting EAs on streams isn't supported. */
|
|
if (fsp_is_alternate_stream(fsp)) {
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/*
|
|
* Filter out invalid Windows EA names - before
|
|
* we set *any* of them.
|
|
*/
|
|
|
|
if (!posix_pathnames && ea_list_has_invalid_name(ea_list)) {
|
|
return STATUS_INVALID_EA_NAME;
|
|
}
|
|
|
|
for (;ea_list; ea_list = ea_list->next) {
|
|
int ret;
|
|
fstring unix_ea_name;
|
|
|
|
/*
|
|
* Complementing the forward mapping from POSIX EAs to
|
|
* Windows EAs in get_ea_list_from_fsp(), here we map in the
|
|
* opposite direction from Windows EAs to the 'user' namespace
|
|
* of POSIX EAs. Hence, all POSIX EA names the we set here must
|
|
* start with a 'user.' prefix.
|
|
*/
|
|
fstrcpy(unix_ea_name, "user.");
|
|
fstrcat(unix_ea_name, ea_list->ea.name);
|
|
|
|
canonicalize_ea_name(fsp, unix_ea_name);
|
|
|
|
DEBUG(10,("set_ea: ea_name %s ealen = %u\n", unix_ea_name, (unsigned int)ea_list->ea.value.length));
|
|
|
|
if (samba_private_attr_name(unix_ea_name)) {
|
|
DEBUG(10,("set_ea: ea name %s is a private Samba name.\n", unix_ea_name));
|
|
return NT_STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
if (ea_list->ea.value.length == 0) {
|
|
/* Remove the attribute. */
|
|
DBG_DEBUG("deleting ea name %s on "
|
|
"file %s by file descriptor.\n",
|
|
unix_ea_name, fsp_str_dbg(fsp));
|
|
ret = SMB_VFS_FREMOVEXATTR(fsp, unix_ea_name);
|
|
#ifdef ENOATTR
|
|
/* Removing a non existent attribute always succeeds. */
|
|
if (ret == -1 && errno == ENOATTR) {
|
|
DEBUG(10,("set_ea: deleting ea name %s didn't exist - succeeding by default.\n",
|
|
unix_ea_name));
|
|
ret = 0;
|
|
}
|
|
#endif
|
|
} else {
|
|
DEBUG(10,("set_ea: setting ea name %s on file "
|
|
"%s by file descriptor.\n",
|
|
unix_ea_name, fsp_str_dbg(fsp)));
|
|
ret = SMB_VFS_FSETXATTR(fsp, unix_ea_name,
|
|
ea_list->ea.value.data, ea_list->ea.value.length, 0);
|
|
}
|
|
|
|
if (ret == -1) {
|
|
#ifdef ENOTSUP
|
|
if (errno == ENOTSUP) {
|
|
return NT_STATUS_EAS_NOT_SUPPORTED;
|
|
}
|
|
#endif
|
|
return map_nt_error_from_unix(errno);
|
|
}
|
|
|
|
}
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Read a list of EA names and data from an incoming data buffer. Create an ea_list with them.
|
|
****************************************************************************/
|
|
|
|
struct ea_list *read_ea_list(TALLOC_CTX *ctx, const char *pdata, size_t data_size)
|
|
{
|
|
struct ea_list *ea_list_head = NULL;
|
|
size_t offset = 0;
|
|
size_t bytes_used = 0;
|
|
|
|
while (offset < data_size) {
|
|
struct ea_list *eal = read_ea_list_entry(ctx, pdata + offset, data_size - offset, &bytes_used);
|
|
|
|
if (!eal) {
|
|
return NULL;
|
|
}
|
|
|
|
DLIST_ADD_END(ea_list_head, eal);
|
|
offset += bytes_used;
|
|
}
|
|
|
|
return ea_list_head;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Count the total EA size needed.
|
|
****************************************************************************/
|
|
|
|
static size_t ea_list_size(struct ea_list *ealist)
|
|
{
|
|
fstring dos_ea_name;
|
|
struct ea_list *listp;
|
|
size_t ret = 0;
|
|
|
|
for (listp = ealist; listp; listp = listp->next) {
|
|
push_ascii_fstring(dos_ea_name, listp->ea.name);
|
|
ret += 4 + strlen(dos_ea_name) + 1 + listp->ea.value.length;
|
|
}
|
|
/* Add on 4 for total length. */
|
|
if (ret) {
|
|
ret += 4;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Return a union of EA's from a file list and a list of names.
|
|
The TALLOC context for the two lists *MUST* be identical as we steal
|
|
memory from one list to add to another. JRA.
|
|
****************************************************************************/
|
|
|
|
static struct ea_list *ea_list_union(struct ea_list *name_list, struct ea_list *file_list, size_t *total_ea_len)
|
|
{
|
|
struct ea_list *nlistp, *flistp;
|
|
|
|
for (nlistp = name_list; nlistp; nlistp = nlistp->next) {
|
|
for (flistp = file_list; flistp; flistp = flistp->next) {
|
|
if (strequal(nlistp->ea.name, flistp->ea.name)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (flistp) {
|
|
/* Copy the data from this entry. */
|
|
nlistp->ea.flags = flistp->ea.flags;
|
|
nlistp->ea.value = flistp->ea.value;
|
|
} else {
|
|
/* Null entry. */
|
|
nlistp->ea.flags = 0;
|
|
ZERO_STRUCT(nlistp->ea.value);
|
|
}
|
|
}
|
|
|
|
*total_ea_len = ea_list_size(name_list);
|
|
return name_list;
|
|
}
|
|
|
|
/*********************************************************
|
|
Routine to check if a given string matches exactly.
|
|
as a special case a mask of "." does NOT match. That
|
|
is required for correct wildcard semantics
|
|
Case can be significant or not.
|
|
**********************************************************/
|
|
|
|
static bool exact_match(bool has_wild,
|
|
bool case_sensitive,
|
|
const char *str,
|
|
const char *mask)
|
|
{
|
|
if (mask[0] == '.' && mask[1] == 0) {
|
|
return false;
|
|
}
|
|
|
|
if (has_wild) {
|
|
return false;
|
|
}
|
|
|
|
if (case_sensitive) {
|
|
return strcmp(str,mask)==0;
|
|
} else {
|
|
return strcasecmp_m(str,mask) == 0;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Return the filetype for UNIX extensions.
|
|
****************************************************************************/
|
|
|
|
static uint32_t unix_filetype(mode_t mode)
|
|
{
|
|
if(S_ISREG(mode))
|
|
return UNIX_TYPE_FILE;
|
|
else if(S_ISDIR(mode))
|
|
return UNIX_TYPE_DIR;
|
|
#ifdef S_ISLNK
|
|
else if(S_ISLNK(mode))
|
|
return UNIX_TYPE_SYMLINK;
|
|
#endif
|
|
#ifdef S_ISCHR
|
|
else if(S_ISCHR(mode))
|
|
return UNIX_TYPE_CHARDEV;
|
|
#endif
|
|
#ifdef S_ISBLK
|
|
else if(S_ISBLK(mode))
|
|
return UNIX_TYPE_BLKDEV;
|
|
#endif
|
|
#ifdef S_ISFIFO
|
|
else if(S_ISFIFO(mode))
|
|
return UNIX_TYPE_FIFO;
|
|
#endif
|
|
#ifdef S_ISSOCK
|
|
else if(S_ISSOCK(mode))
|
|
return UNIX_TYPE_SOCKET;
|
|
#endif
|
|
|
|
DEBUG(0,("unix_filetype: unknown filetype %u\n", (unsigned)mode));
|
|
return UNIX_TYPE_UNKNOWN;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Map wire perms onto standard UNIX permissions. Obey share restrictions.
|
|
****************************************************************************/
|
|
|
|
NTSTATUS unix_perms_from_wire(connection_struct *conn,
|
|
const SMB_STRUCT_STAT *psbuf,
|
|
uint32_t perms,
|
|
enum perm_type ptype,
|
|
mode_t *ret_perms)
|
|
{
|
|
mode_t ret = 0;
|
|
|
|
if (perms == SMB_MODE_NO_CHANGE) {
|
|
if (!VALID_STAT(*psbuf)) {
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
} else {
|
|
*ret_perms = psbuf->st_ex_mode;
|
|
return NT_STATUS_OK;
|
|
}
|
|
}
|
|
|
|
ret = wire_perms_to_unix(perms);
|
|
|
|
if (ptype == PERM_NEW_FILE) {
|
|
/*
|
|
* "create mask"/"force create mode" are
|
|
* only applied to new files, not existing ones.
|
|
*/
|
|
ret &= lp_create_mask(SNUM(conn));
|
|
/* Add in force bits */
|
|
ret |= lp_force_create_mode(SNUM(conn));
|
|
} else if (ptype == PERM_NEW_DIR) {
|
|
/*
|
|
* "directory mask"/"force directory mode" are
|
|
* only applied to new directories, not existing ones.
|
|
*/
|
|
ret &= lp_directory_mask(SNUM(conn));
|
|
/* Add in force bits */
|
|
ret |= lp_force_directory_mode(SNUM(conn));
|
|
}
|
|
|
|
*ret_perms = ret;
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Needed to show the msdfs symlinks as directories. Modifies psbuf
|
|
to be a directory if it's a msdfs link.
|
|
****************************************************************************/
|
|
|
|
static bool check_msdfs_link(struct files_struct *dirfsp,
|
|
struct smb_filename *atname,
|
|
struct smb_filename *smb_fname)
|
|
{
|
|
int saved_errno = errno;
|
|
if(lp_host_msdfs() &&
|
|
lp_msdfs_root(SNUM(dirfsp->conn)) &&
|
|
is_msdfs_link(dirfsp, atname)) {
|
|
|
|
/*
|
|
* Copy the returned stat struct from the relative
|
|
* to the full pathname.
|
|
*/
|
|
smb_fname->st = atname->st;
|
|
|
|
DEBUG(5,("check_msdfs_link: Masquerading msdfs link %s "
|
|
"as a directory\n",
|
|
smb_fname->base_name));
|
|
smb_fname->st.st_ex_mode =
|
|
(smb_fname->st.st_ex_mode & 0xFFF) | S_IFDIR;
|
|
errno = saved_errno;
|
|
return true;
|
|
}
|
|
errno = saved_errno;
|
|
return false;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
Get a level dependent lanman2 dir entry.
|
|
****************************************************************************/
|
|
|
|
struct smbd_dirptr_lanman2_state {
|
|
connection_struct *conn;
|
|
uint32_t info_level;
|
|
bool check_mangled_names;
|
|
bool has_wild;
|
|
bool got_exact_match;
|
|
bool case_sensitive;
|
|
};
|
|
|
|
static bool smbd_dirptr_lanman2_match_fn(TALLOC_CTX *ctx,
|
|
void *private_data,
|
|
const char *dname,
|
|
const char *mask,
|
|
char **_fname)
|
|
{
|
|
struct smbd_dirptr_lanman2_state *state =
|
|
(struct smbd_dirptr_lanman2_state *)private_data;
|
|
bool ok;
|
|
char mangled_name[13]; /* mangled 8.3 name. */
|
|
bool got_match;
|
|
const char *fname;
|
|
|
|
/* Mangle fname if it's an illegal name. */
|
|
if (mangle_must_mangle(dname, state->conn->params)) {
|
|
/*
|
|
* Slow path - ensure we can push the original name as UCS2. If
|
|
* not, then just don't return this name.
|
|
*/
|
|
NTSTATUS status;
|
|
size_t ret_len = 0;
|
|
size_t len = (strlen(dname) + 2) * 4; /* Allow enough space. */
|
|
uint8_t *tmp = talloc_array(talloc_tos(),
|
|
uint8_t,
|
|
len);
|
|
|
|
status = srvstr_push(NULL,
|
|
FLAGS2_UNICODE_STRINGS,
|
|
tmp,
|
|
dname,
|
|
len,
|
|
STR_TERMINATE,
|
|
&ret_len);
|
|
|
|
TALLOC_FREE(tmp);
|
|
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return false;
|
|
}
|
|
|
|
ok = name_to_8_3(dname, mangled_name,
|
|
true, state->conn->params);
|
|
if (!ok) {
|
|
return false;
|
|
}
|
|
fname = mangled_name;
|
|
} else {
|
|
fname = dname;
|
|
}
|
|
|
|
got_match = exact_match(state->has_wild,
|
|
state->case_sensitive,
|
|
fname, mask);
|
|
state->got_exact_match = got_match;
|
|
if (!got_match) {
|
|
got_match = mask_match(fname, mask,
|
|
state->case_sensitive);
|
|
}
|
|
|
|
if(!got_match && state->check_mangled_names &&
|
|
!mangle_is_8_3(fname, false, state->conn->params)) {
|
|
/*
|
|
* It turns out that NT matches wildcards against
|
|
* both long *and* short names. This may explain some
|
|
* of the wildcard wierdness from old DOS clients
|
|
* that some people have been seeing.... JRA.
|
|
*/
|
|
/* Force the mangling into 8.3. */
|
|
ok = name_to_8_3(fname, mangled_name,
|
|
false, state->conn->params);
|
|
if (!ok) {
|
|
return false;
|
|
}
|
|
|
|
got_match = exact_match(state->has_wild,
|
|
state->case_sensitive,
|
|
mangled_name, mask);
|
|
state->got_exact_match = got_match;
|
|
if (!got_match) {
|
|
got_match = mask_match(mangled_name, mask,
|
|
state->case_sensitive);
|
|
}
|
|
}
|
|
|
|
if (!got_match) {
|
|
return false;
|
|
}
|
|
|
|
*_fname = talloc_strdup(ctx, fname);
|
|
if (*_fname == NULL) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool smbd_dirptr_lanman2_mode_fn(TALLOC_CTX *ctx,
|
|
void *private_data,
|
|
struct files_struct *dirfsp,
|
|
struct smb_filename *atname,
|
|
struct smb_filename *smb_fname,
|
|
bool get_dosmode,
|
|
uint32_t *_mode)
|
|
{
|
|
struct smbd_dirptr_lanman2_state *state =
|
|
(struct smbd_dirptr_lanman2_state *)private_data;
|
|
bool ms_dfs_link = false;
|
|
|
|
if (smb_fname->flags & SMB_FILENAME_POSIX_PATH) {
|
|
if (SMB_VFS_LSTAT(state->conn, smb_fname) != 0) {
|
|
DEBUG(5,("smbd_dirptr_lanman2_mode_fn: "
|
|
"Couldn't lstat [%s] (%s)\n",
|
|
smb_fname_str_dbg(smb_fname),
|
|
strerror(errno)));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if (!VALID_STAT(smb_fname->st) &&
|
|
SMB_VFS_STAT(state->conn, smb_fname) != 0) {
|
|
/* Needed to show the msdfs symlinks as
|
|
* directories */
|
|
|
|
ms_dfs_link = check_msdfs_link(dirfsp,
|
|
atname,
|
|
smb_fname);
|
|
if (!ms_dfs_link) {
|
|
DEBUG(5,("smbd_dirptr_lanman2_mode_fn: "
|
|
"Couldn't stat [%s] (%s)\n",
|
|
smb_fname_str_dbg(smb_fname),
|
|
strerror(errno)));
|
|
return false;
|
|
}
|
|
|
|
*_mode = dos_mode_msdfs(state->conn, smb_fname);
|
|
return true;
|
|
}
|
|
|
|
if (!get_dosmode) {
|
|
return true;
|
|
}
|
|
|
|
*_mode = fdos_mode(smb_fname->fsp);
|
|
smb_fname->st = smb_fname->fsp->fsp_name->st;
|
|
|
|
return true;
|
|
}
|
|
|
|
static NTSTATUS smbd_marshall_dir_entry(TALLOC_CTX *ctx,
|
|
connection_struct *conn,
|
|
uint16_t flags2,
|
|
uint32_t info_level,
|
|
struct ea_list *name_list,
|
|
bool check_mangled_names,
|
|
bool requires_resume_key,
|
|
uint32_t mode,
|
|
const char *fname,
|
|
const struct smb_filename *smb_fname,
|
|
int space_remaining,
|
|
uint8_t align,
|
|
bool do_pad,
|
|
char *base_data,
|
|
char **ppdata,
|
|
char *end_data,
|
|
uint64_t *last_entry_off)
|
|
{
|
|
char *p, *q, *pdata = *ppdata;
|
|
uint32_t reskey=0;
|
|
uint64_t file_size = 0;
|
|
uint64_t allocation_size = 0;
|
|
uint64_t file_id = 0;
|
|
size_t len = 0;
|
|
struct timespec mdate_ts = {0};
|
|
struct timespec adate_ts = {0};
|
|
struct timespec cdate_ts = {0};
|
|
struct timespec create_date_ts = {0};
|
|
time_t mdate = (time_t)0, adate = (time_t)0, create_date = (time_t)0;
|
|
char *nameptr;
|
|
char *last_entry_ptr;
|
|
bool was_8_3;
|
|
int off;
|
|
int pad = 0;
|
|
NTSTATUS status;
|
|
struct readdir_attr_data *readdir_attr_data = NULL;
|
|
|
|
if (!(mode & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
file_size = get_file_size_stat(&smb_fname->st);
|
|
}
|
|
allocation_size = SMB_VFS_GET_ALLOC_SIZE(conn, NULL, &smb_fname->st);
|
|
|
|
/*
|
|
* Skip SMB_VFS_FREADDIR_ATTR if the directory entry is a symlink or
|
|
* a DFS symlink.
|
|
*/
|
|
if (smb_fname->fsp != NULL &&
|
|
!(mode & FILE_ATTRIBUTE_REPARSE_POINT)) {
|
|
status = SMB_VFS_FREADDIR_ATTR(smb_fname->fsp,
|
|
ctx,
|
|
&readdir_attr_data);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
if (!NT_STATUS_EQUAL(NT_STATUS_NOT_SUPPORTED,
|
|
status)) {
|
|
return status;
|
|
}
|
|
}
|
|
}
|
|
|
|
file_id = SMB_VFS_FS_FILE_ID(conn, &smb_fname->st);
|
|
|
|
mdate_ts = smb_fname->st.st_ex_mtime;
|
|
adate_ts = smb_fname->st.st_ex_atime;
|
|
create_date_ts = get_create_timespec(conn, NULL, smb_fname);
|
|
cdate_ts = get_change_timespec(conn, NULL, smb_fname);
|
|
|
|
if (lp_dos_filetime_resolution(SNUM(conn))) {
|
|
dos_filetime_timespec(&create_date_ts);
|
|
dos_filetime_timespec(&mdate_ts);
|
|
dos_filetime_timespec(&adate_ts);
|
|
dos_filetime_timespec(&cdate_ts);
|
|
}
|
|
|
|
create_date = convert_timespec_to_time_t(create_date_ts);
|
|
mdate = convert_timespec_to_time_t(mdate_ts);
|
|
adate = convert_timespec_to_time_t(adate_ts);
|
|
|
|
/* align the record */
|
|
SMB_ASSERT(align >= 1);
|
|
|
|
off = (int)PTR_DIFF(pdata, base_data);
|
|
pad = (off + (align-1)) & ~(align-1);
|
|
pad -= off;
|
|
|
|
if (pad && pad > space_remaining) {
|
|
DEBUG(9,("smbd_marshall_dir_entry: out of space "
|
|
"for padding (wanted %u, had %d)\n",
|
|
(unsigned int)pad,
|
|
space_remaining ));
|
|
return STATUS_MORE_ENTRIES; /* Not finished - just out of space */
|
|
}
|
|
|
|
off += pad;
|
|
/* initialize padding to 0 */
|
|
if (pad) {
|
|
memset(pdata, 0, pad);
|
|
}
|
|
space_remaining -= pad;
|
|
|
|
DEBUG(10,("smbd_marshall_dir_entry: space_remaining = %d\n",
|
|
space_remaining ));
|
|
|
|
pdata += pad;
|
|
p = pdata;
|
|
last_entry_ptr = p;
|
|
|
|
pad = 0;
|
|
off = 0;
|
|
|
|
switch (info_level) {
|
|
case SMB_FIND_INFO_STANDARD:
|
|
DEBUG(10,("smbd_marshall_dir_entry: SMB_FIND_INFO_STANDARD\n"));
|
|
if(requires_resume_key) {
|
|
SIVAL(p,0,reskey);
|
|
p += 4;
|
|
}
|
|
srv_put_dos_date2(p,0,create_date);
|
|
srv_put_dos_date2(p,4,adate);
|
|
srv_put_dos_date2(p,8,mdate);
|
|
SIVAL(p,12,(uint32_t)file_size);
|
|
SIVAL(p,16,(uint32_t)allocation_size);
|
|
SSVAL(p,20,mode);
|
|
p += 23;
|
|
nameptr = p;
|
|
if (flags2 & FLAGS2_UNICODE_STRINGS) {
|
|
p += ucs2_align(base_data, p, 0);
|
|
}
|
|
status = srvstr_push(base_data, flags2, p,
|
|
fname, PTR_DIFF(end_data, p),
|
|
STR_TERMINATE, &len);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
if (flags2 & FLAGS2_UNICODE_STRINGS) {
|
|
if (len > 2) {
|
|
SCVAL(nameptr, -1, len - 2);
|
|
} else {
|
|
SCVAL(nameptr, -1, 0);
|
|
}
|
|
} else {
|
|
if (len > 1) {
|
|
SCVAL(nameptr, -1, len - 1);
|
|
} else {
|
|
SCVAL(nameptr, -1, 0);
|
|
}
|
|
}
|
|
p += len;
|
|
break;
|
|
|
|
case SMB_FIND_EA_SIZE:
|
|
DEBUG(10,("smbd_marshall_dir_entry: SMB_FIND_EA_SIZE\n"));
|
|
if (requires_resume_key) {
|
|
SIVAL(p,0,reskey);
|
|
p += 4;
|
|
}
|
|
srv_put_dos_date2(p,0,create_date);
|
|
srv_put_dos_date2(p,4,adate);
|
|
srv_put_dos_date2(p,8,mdate);
|
|
SIVAL(p,12,(uint32_t)file_size);
|
|
SIVAL(p,16,(uint32_t)allocation_size);
|
|
SSVAL(p,20,mode);
|
|
{
|
|
unsigned int ea_size = estimate_ea_size(smb_fname->fsp);
|
|
SIVAL(p,22,ea_size); /* Extended attributes */
|
|
}
|
|
p += 27;
|
|
nameptr = p - 1;
|
|
status = srvstr_push(base_data, flags2,
|
|
p, fname, PTR_DIFF(end_data, p),
|
|
STR_TERMINATE | STR_NOALIGN, &len);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
if (flags2 & FLAGS2_UNICODE_STRINGS) {
|
|
if (len > 2) {
|
|
len -= 2;
|
|
} else {
|
|
len = 0;
|
|
}
|
|
} else {
|
|
if (len > 1) {
|
|
len -= 1;
|
|
} else {
|
|
len = 0;
|
|
}
|
|
}
|
|
SCVAL(nameptr,0,len);
|
|
p += len;
|
|
SCVAL(p,0,0); p += 1; /* Extra zero byte ? - why.. */
|
|
break;
|
|
|
|
case SMB_FIND_EA_LIST:
|
|
{
|
|
struct ea_list *file_list = NULL;
|
|
size_t ea_len = 0;
|
|
|
|
DEBUG(10,("smbd_marshall_dir_entry: SMB_FIND_EA_LIST\n"));
|
|
if (!name_list) {
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
if (requires_resume_key) {
|
|
SIVAL(p,0,reskey);
|
|
p += 4;
|
|
}
|
|
srv_put_dos_date2(p,0,create_date);
|
|
srv_put_dos_date2(p,4,adate);
|
|
srv_put_dos_date2(p,8,mdate);
|
|
SIVAL(p,12,(uint32_t)file_size);
|
|
SIVAL(p,16,(uint32_t)allocation_size);
|
|
SSVAL(p,20,mode);
|
|
p += 22; /* p now points to the EA area. */
|
|
|
|
status = get_ea_list_from_fsp(ctx,
|
|
smb_fname->fsp,
|
|
&ea_len, &file_list);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
file_list = NULL;
|
|
}
|
|
name_list = ea_list_union(name_list, file_list, &ea_len);
|
|
|
|
/* We need to determine if this entry will fit in the space available. */
|
|
/* Max string size is 255 bytes. */
|
|
if (PTR_DIFF(p + 255 + ea_len,pdata) > space_remaining) {
|
|
DEBUG(9,("smbd_marshall_dir_entry: out of space "
|
|
"(wanted %u, had %d)\n",
|
|
(unsigned int)PTR_DIFF(p + 255 + ea_len,pdata),
|
|
space_remaining ));
|
|
return STATUS_MORE_ENTRIES; /* Not finished - just out of space */
|
|
}
|
|
|
|
/* Push the ea_data followed by the name. */
|
|
p += fill_ea_buffer(ctx, p, space_remaining, conn, name_list);
|
|
nameptr = p;
|
|
status = srvstr_push(base_data, flags2,
|
|
p + 1, fname, PTR_DIFF(end_data, p+1),
|
|
STR_TERMINATE | STR_NOALIGN, &len);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
if (flags2 & FLAGS2_UNICODE_STRINGS) {
|
|
if (len > 2) {
|
|
len -= 2;
|
|
} else {
|
|
len = 0;
|
|
}
|
|
} else {
|
|
if (len > 1) {
|
|
len -= 1;
|
|
} else {
|
|
len = 0;
|
|
}
|
|
}
|
|
SCVAL(nameptr,0,len);
|
|
p += len + 1;
|
|
SCVAL(p,0,0); p += 1; /* Extra zero byte ? - why.. */
|
|
break;
|
|
}
|
|
|
|
case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
|
|
DEBUG(10,("smbd_marshall_dir_entry: SMB_FIND_FILE_BOTH_DIRECTORY_INFO\n"));
|
|
was_8_3 = mangle_is_8_3(fname, True, conn->params);
|
|
p += 4;
|
|
SIVAL(p,0,reskey); p += 4;
|
|
put_long_date_full_timespec(conn->ts_res,p,&create_date_ts); p += 8;
|
|
put_long_date_full_timespec(conn->ts_res,p,&adate_ts); p += 8;
|
|
put_long_date_full_timespec(conn->ts_res,p,&mdate_ts); p += 8;
|
|
put_long_date_full_timespec(conn->ts_res,p,&cdate_ts); p += 8;
|
|
SOFF_T(p,0,file_size); p += 8;
|
|
SOFF_T(p,0,allocation_size); p += 8;
|
|
SIVAL(p,0,mode); p += 4;
|
|
q = p; p += 4; /* q is placeholder for name length. */
|
|
if (mode & FILE_ATTRIBUTE_REPARSE_POINT) {
|
|
SIVAL(p, 0, IO_REPARSE_TAG_DFS);
|
|
} else {
|
|
unsigned int ea_size = estimate_ea_size(smb_fname->fsp);
|
|
SIVAL(p,0,ea_size); /* Extended attributes */
|
|
}
|
|
p += 4;
|
|
/* Clear the short name buffer. This is
|
|
* IMPORTANT as not doing so will trigger
|
|
* a Win2k client bug. JRA.
|
|
*/
|
|
if (!was_8_3 && check_mangled_names) {
|
|
char mangled_name[13]; /* mangled 8.3 name. */
|
|
if (!name_to_8_3(fname,mangled_name,True,
|
|
conn->params)) {
|
|
/* Error - mangle failed ! */
|
|
memset(mangled_name,'\0',12);
|
|
}
|
|
mangled_name[12] = 0;
|
|
status = srvstr_push(base_data, flags2,
|
|
p+2, mangled_name, 24,
|
|
STR_UPPER|STR_UNICODE, &len);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
if (len < 24) {
|
|
memset(p + 2 + len,'\0',24 - len);
|
|
}
|
|
SSVAL(p, 0, len);
|
|
} else {
|
|
memset(p,'\0',26);
|
|
}
|
|
p += 2 + 24;
|
|
status = srvstr_push(base_data, flags2, p,
|
|
fname, PTR_DIFF(end_data, p),
|
|
STR_TERMINATE_ASCII, &len);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
SIVAL(q,0,len);
|
|
p += len;
|
|
|
|
len = PTR_DIFF(p, pdata);
|
|
pad = (len + (align-1)) & ~(align-1);
|
|
/*
|
|
* offset to the next entry, the caller
|
|
* will overwrite it for the last entry
|
|
* that's why we always include the padding
|
|
*/
|
|
SIVAL(pdata,0,pad);
|
|
/*
|
|
* set padding to zero
|
|
*/
|
|
if (do_pad) {
|
|
memset(p, 0, pad - len);
|
|
p = pdata + pad;
|
|
} else {
|
|
p = pdata + len;
|
|
}
|
|
break;
|
|
|
|
case SMB_FIND_FILE_DIRECTORY_INFO:
|
|
DEBUG(10,("smbd_marshall_dir_entry: SMB_FIND_FILE_DIRECTORY_INFO\n"));
|
|
p += 4;
|
|
SIVAL(p,0,reskey); p += 4;
|
|
put_long_date_full_timespec(conn->ts_res,p,&create_date_ts); p += 8;
|
|
put_long_date_full_timespec(conn->ts_res,p,&adate_ts); p += 8;
|
|
put_long_date_full_timespec(conn->ts_res,p,&mdate_ts); p += 8;
|
|
put_long_date_full_timespec(conn->ts_res,p,&cdate_ts); p += 8;
|
|
SOFF_T(p,0,file_size); p += 8;
|
|
SOFF_T(p,0,allocation_size); p += 8;
|
|
SIVAL(p,0,mode); p += 4;
|
|
status = srvstr_push(base_data, flags2,
|
|
p + 4, fname, PTR_DIFF(end_data, p+4),
|
|
STR_TERMINATE_ASCII, &len);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
SIVAL(p,0,len);
|
|
p += 4 + len;
|
|
|
|
len = PTR_DIFF(p, pdata);
|
|
pad = (len + (align-1)) & ~(align-1);
|
|
/*
|
|
* offset to the next entry, the caller
|
|
* will overwrite it for the last entry
|
|
* that's why we always include the padding
|
|
*/
|
|
SIVAL(pdata,0,pad);
|
|
/*
|
|
* set padding to zero
|
|
*/
|
|
if (do_pad) {
|
|
memset(p, 0, pad - len);
|
|
p = pdata + pad;
|
|
} else {
|
|
p = pdata + len;
|
|
}
|
|
break;
|
|
|
|
case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
|
|
DEBUG(10,("smbd_marshall_dir_entry: SMB_FIND_FILE_FULL_DIRECTORY_INFO\n"));
|
|
p += 4;
|
|
SIVAL(p,0,reskey); p += 4;
|
|
put_long_date_full_timespec(conn->ts_res,p,&create_date_ts); p += 8;
|
|
put_long_date_full_timespec(conn->ts_res,p,&adate_ts); p += 8;
|
|
put_long_date_full_timespec(conn->ts_res,p,&mdate_ts); p += 8;
|
|
put_long_date_full_timespec(conn->ts_res,p,&cdate_ts); p += 8;
|
|
SOFF_T(p,0,file_size); p += 8;
|
|
SOFF_T(p,0,allocation_size); p += 8;
|
|
SIVAL(p,0,mode); p += 4;
|
|
q = p; p += 4; /* q is placeholder for name length. */
|
|
if (mode & FILE_ATTRIBUTE_REPARSE_POINT) {
|
|
SIVAL(p, 0, IO_REPARSE_TAG_DFS);
|
|
} else {
|
|
unsigned int ea_size = estimate_ea_size(smb_fname->fsp);
|
|
SIVAL(p,0,ea_size); /* Extended attributes */
|
|
}
|
|
p +=4;
|
|
status = srvstr_push(base_data, flags2, p,
|
|
fname, PTR_DIFF(end_data, p),
|
|
STR_TERMINATE_ASCII, &len);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
SIVAL(q, 0, len);
|
|
p += len;
|
|
|
|
len = PTR_DIFF(p, pdata);
|
|
pad = (len + (align-1)) & ~(align-1);
|
|
/*
|
|
* offset to the next entry, the caller
|
|
* will overwrite it for the last entry
|
|
* that's why we always include the padding
|
|
*/
|
|
SIVAL(pdata,0,pad);
|
|
/*
|
|
* set padding to zero
|
|
*/
|
|
if (do_pad) {
|
|
memset(p, 0, pad - len);
|
|
p = pdata + pad;
|
|
} else {
|
|
p = pdata + len;
|
|
}
|
|
break;
|
|
|
|
case SMB_FIND_FILE_NAMES_INFO:
|
|
DEBUG(10,("smbd_marshall_dir_entry: SMB_FIND_FILE_NAMES_INFO\n"));
|
|
p += 4;
|
|
SIVAL(p,0,reskey); p += 4;
|
|
p += 4;
|
|
/* this must *not* be null terminated or w2k gets in a loop trying to set an
|
|
acl on a dir (tridge) */
|
|
status = srvstr_push(base_data, flags2, p,
|
|
fname, PTR_DIFF(end_data, p),
|
|
STR_TERMINATE_ASCII, &len);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
SIVAL(p, -4, len);
|
|
p += len;
|
|
|
|
len = PTR_DIFF(p, pdata);
|
|
pad = (len + (align-1)) & ~(align-1);
|
|
/*
|
|
* offset to the next entry, the caller
|
|
* will overwrite it for the last entry
|
|
* that's why we always include the padding
|
|
*/
|
|
SIVAL(pdata,0,pad);
|
|
/*
|
|
* set padding to zero
|
|
*/
|
|
if (do_pad) {
|
|
memset(p, 0, pad - len);
|
|
p = pdata + pad;
|
|
} else {
|
|
p = pdata + len;
|
|
}
|
|
break;
|
|
|
|
case SMB_FIND_ID_FULL_DIRECTORY_INFO:
|
|
DEBUG(10,("smbd_marshall_dir_entry: SMB_FIND_ID_FULL_DIRECTORY_INFO\n"));
|
|
p += 4;
|
|
SIVAL(p,0,reskey); p += 4;
|
|
put_long_date_full_timespec(conn->ts_res,p,&create_date_ts); p += 8;
|
|
put_long_date_full_timespec(conn->ts_res,p,&adate_ts); p += 8;
|
|
put_long_date_full_timespec(conn->ts_res,p,&mdate_ts); p += 8;
|
|
put_long_date_full_timespec(conn->ts_res,p,&cdate_ts); p += 8;
|
|
SOFF_T(p,0,file_size); p += 8;
|
|
SOFF_T(p,0,allocation_size); p += 8;
|
|
SIVAL(p,0,mode); p += 4;
|
|
q = p; p += 4; /* q is placeholder for name length. */
|
|
if (mode & FILE_ATTRIBUTE_REPARSE_POINT) {
|
|
SIVAL(p, 0, IO_REPARSE_TAG_DFS);
|
|
} else {
|
|
unsigned int ea_size = estimate_ea_size(smb_fname->fsp);
|
|
SIVAL(p,0,ea_size); /* Extended attributes */
|
|
}
|
|
p += 4;
|
|
SIVAL(p,0,0); p += 4; /* Unknown - reserved ? */
|
|
SBVAL(p,0,file_id); p += 8;
|
|
status = srvstr_push(base_data, flags2, p,
|
|
fname, PTR_DIFF(end_data, p),
|
|
STR_TERMINATE_ASCII, &len);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
SIVAL(q, 0, len);
|
|
p += len;
|
|
|
|
len = PTR_DIFF(p, pdata);
|
|
pad = (len + (align-1)) & ~(align-1);
|
|
/*
|
|
* offset to the next entry, the caller
|
|
* will overwrite it for the last entry
|
|
* that's why we always include the padding
|
|
*/
|
|
SIVAL(pdata,0,pad);
|
|
/*
|
|
* set padding to zero
|
|
*/
|
|
if (do_pad) {
|
|
memset(p, 0, pad - len);
|
|
p = pdata + pad;
|
|
} else {
|
|
p = pdata + len;
|
|
}
|
|
break;
|
|
|
|
case SMB_FIND_ID_BOTH_DIRECTORY_INFO:
|
|
DEBUG(10,("smbd_marshall_dir_entry: SMB_FIND_ID_BOTH_DIRECTORY_INFO\n"));
|
|
was_8_3 = mangle_is_8_3(fname, True, conn->params);
|
|
p += 4;
|
|
SIVAL(p,0,reskey); p += 4;
|
|
put_long_date_full_timespec(conn->ts_res,p,&create_date_ts); p += 8;
|
|
put_long_date_full_timespec(conn->ts_res,p,&adate_ts); p += 8;
|
|
put_long_date_full_timespec(conn->ts_res,p,&mdate_ts); p += 8;
|
|
put_long_date_full_timespec(conn->ts_res,p,&cdate_ts); p += 8;
|
|
SOFF_T(p,0,file_size); p += 8;
|
|
SOFF_T(p,0,allocation_size); p += 8;
|
|
SIVAL(p,0,mode); p += 4;
|
|
q = p; p += 4; /* q is placeholder for name length */
|
|
if (mode & FILE_ATTRIBUTE_REPARSE_POINT) {
|
|
SIVAL(p, 0, IO_REPARSE_TAG_DFS);
|
|
} else if (readdir_attr_data &&
|
|
readdir_attr_data->type == RDATTR_AAPL) {
|
|
/*
|
|
* OS X specific SMB2 extension negotiated via
|
|
* AAPL create context: return max_access in
|
|
* ea_size field.
|
|
*/
|
|
SIVAL(p, 0, readdir_attr_data->attr_data.aapl.max_access);
|
|
} else {
|
|
unsigned int ea_size = estimate_ea_size(smb_fname->fsp);
|
|
SIVAL(p,0,ea_size); /* Extended attributes */
|
|
}
|
|
p += 4;
|
|
|
|
if (readdir_attr_data &&
|
|
readdir_attr_data->type == RDATTR_AAPL) {
|
|
/*
|
|
* OS X specific SMB2 extension negotiated via
|
|
* AAPL create context: return resource fork
|
|
* length and compressed FinderInfo in
|
|
* shortname field.
|
|
*
|
|
* According to documentation short_name_len
|
|
* should be 0, but on the wire behaviour
|
|
* shows its set to 24 by clients.
|
|
*/
|
|
SSVAL(p, 0, 24);
|
|
|
|
/* Resourefork length */
|
|
SBVAL(p, 2, readdir_attr_data->attr_data.aapl.rfork_size);
|
|
|
|
/* Compressed FinderInfo */
|
|
memcpy(p + 10, &readdir_attr_data->attr_data.aapl.finder_info, 16);
|
|
} else if (!was_8_3 && check_mangled_names) {
|
|
char mangled_name[13]; /* mangled 8.3 name. */
|
|
if (!name_to_8_3(fname,mangled_name,True,
|
|
conn->params)) {
|
|
/* Error - mangle failed ! */
|
|
memset(mangled_name,'\0',12);
|
|
}
|
|
mangled_name[12] = 0;
|
|
status = srvstr_push(base_data, flags2,
|
|
p+2, mangled_name, 24,
|
|
STR_UPPER|STR_UNICODE, &len);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
SSVAL(p, 0, len);
|
|
if (len < 24) {
|
|
memset(p + 2 + len,'\0',24 - len);
|
|
}
|
|
SSVAL(p, 0, len);
|
|
} else {
|
|
/* Clear the short name buffer. This is
|
|
* IMPORTANT as not doing so will trigger
|
|
* a Win2k client bug. JRA.
|
|
*/
|
|
memset(p,'\0',26);
|
|
}
|
|
p += 26;
|
|
|
|
/* Reserved ? */
|
|
if (readdir_attr_data &&
|
|
readdir_attr_data->type == RDATTR_AAPL) {
|
|
/*
|
|
* OS X specific SMB2 extension negotiated via
|
|
* AAPL create context: return UNIX mode in
|
|
* reserved field.
|
|
*/
|
|
uint16_t aapl_mode = (uint16_t)readdir_attr_data->attr_data.aapl.unix_mode;
|
|
SSVAL(p, 0, aapl_mode);
|
|
} else {
|
|
SSVAL(p, 0, 0);
|
|
}
|
|
p += 2;
|
|
|
|
SBVAL(p,0,file_id); p += 8;
|
|
status = srvstr_push(base_data, flags2, p,
|
|
fname, PTR_DIFF(end_data, p),
|
|
STR_TERMINATE_ASCII, &len);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
SIVAL(q,0,len);
|
|
p += len;
|
|
|
|
len = PTR_DIFF(p, pdata);
|
|
pad = (len + (align-1)) & ~(align-1);
|
|
/*
|
|
* offset to the next entry, the caller
|
|
* will overwrite it for the last entry
|
|
* that's why we always include the padding
|
|
*/
|
|
SIVAL(pdata,0,pad);
|
|
/*
|
|
* set padding to zero
|
|
*/
|
|
if (do_pad) {
|
|
memset(p, 0, pad - len);
|
|
p = pdata + pad;
|
|
} else {
|
|
p = pdata + len;
|
|
}
|
|
break;
|
|
|
|
/* CIFS UNIX Extension. */
|
|
|
|
case SMB_FIND_FILE_UNIX:
|
|
case SMB_FIND_FILE_UNIX_INFO2:
|
|
p+= 4;
|
|
SIVAL(p,0,reskey); p+= 4; /* Used for continuing search. */
|
|
|
|
/* Begin of SMB_QUERY_FILE_UNIX_BASIC */
|
|
|
|
if (info_level == SMB_FIND_FILE_UNIX) {
|
|
DEBUG(10,("smbd_marshall_dir_entry: SMB_FIND_FILE_UNIX\n"));
|
|
p = store_file_unix_basic(conn, p,
|
|
NULL, &smb_fname->st);
|
|
status = srvstr_push(base_data, flags2, p,
|
|
fname, PTR_DIFF(end_data, p),
|
|
STR_TERMINATE, &len);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
} else {
|
|
DEBUG(10,("smbd_marshall_dir_entry: SMB_FIND_FILE_UNIX_INFO2\n"));
|
|
p = store_file_unix_basic_info2(conn, p,
|
|
NULL, &smb_fname->st);
|
|
nameptr = p;
|
|
p += 4;
|
|
status = srvstr_push(base_data, flags2, p, fname,
|
|
PTR_DIFF(end_data, p), 0, &len);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
SIVAL(nameptr, 0, len);
|
|
}
|
|
|
|
p += len;
|
|
|
|
len = PTR_DIFF(p, pdata);
|
|
pad = (len + (align-1)) & ~(align-1);
|
|
/*
|
|
* offset to the next entry, the caller
|
|
* will overwrite it for the last entry
|
|
* that's why we always include the padding
|
|
*/
|
|
SIVAL(pdata,0,pad);
|
|
/*
|
|
* set padding to zero
|
|
*/
|
|
if (do_pad) {
|
|
memset(p, 0, pad - len);
|
|
p = pdata + pad;
|
|
} else {
|
|
p = pdata + len;
|
|
}
|
|
/* End of SMB_QUERY_FILE_UNIX_BASIC */
|
|
|
|
break;
|
|
|
|
/* SMB2 UNIX Extension. */
|
|
|
|
case SMB2_FILE_POSIX_INFORMATION:
|
|
{
|
|
uint8_t *buf = NULL;
|
|
ssize_t plen = 0;
|
|
p+= 4;
|
|
SIVAL(p,0,reskey); p+= 4;
|
|
|
|
DEBUG(10,("smbd_marshall_dir_entry: "
|
|
"SMB2_FILE_POSIX_INFORMATION\n"));
|
|
if (!(conn->sconn->using_smb2)) {
|
|
return NT_STATUS_INVALID_LEVEL;
|
|
}
|
|
if (!lp_smb3_unix_extensions()) {
|
|
return NT_STATUS_INVALID_LEVEL;
|
|
}
|
|
|
|
/* Determine the size of the posix info context */
|
|
plen = store_smb2_posix_info(conn,
|
|
&smb_fname->st,
|
|
0,
|
|
mode,
|
|
NULL,
|
|
0);
|
|
if (plen == -1) {
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
buf = talloc_zero_size(ctx, plen);
|
|
if (buf == NULL) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
/* Store the context in buf */
|
|
store_smb2_posix_info(conn,
|
|
&smb_fname->st,
|
|
0,
|
|
mode,
|
|
buf,
|
|
plen);
|
|
memcpy(p, buf, plen);
|
|
p += plen;
|
|
TALLOC_FREE(buf);
|
|
|
|
nameptr = p;
|
|
p += 4;
|
|
status = srvstr_push(base_data, flags2, p, fname,
|
|
PTR_DIFF(end_data, p), 0, &len);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
SIVAL(nameptr, 0, len);
|
|
|
|
p += len;
|
|
|
|
len = PTR_DIFF(p, pdata);
|
|
pad = (len + (align-1)) & ~(align-1);
|
|
/*
|
|
* offset to the next entry, the caller
|
|
* will overwrite it for the last entry
|
|
* that's why we always include the padding
|
|
*/
|
|
SIVAL(pdata,0,pad);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
return NT_STATUS_INVALID_LEVEL;
|
|
}
|
|
|
|
if (PTR_DIFF(p,pdata) > space_remaining) {
|
|
DEBUG(9,("smbd_marshall_dir_entry: out of space "
|
|
"(wanted %u, had %d)\n",
|
|
(unsigned int)PTR_DIFF(p,pdata),
|
|
space_remaining ));
|
|
return STATUS_MORE_ENTRIES; /* Not finished - just out of space */
|
|
}
|
|
|
|
/* Setup the last entry pointer, as an offset from base_data */
|
|
*last_entry_off = PTR_DIFF(last_entry_ptr,base_data);
|
|
/* Advance the data pointer to the next slot */
|
|
*ppdata = p;
|
|
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
NTSTATUS smbd_dirptr_lanman2_entry(TALLOC_CTX *ctx,
|
|
connection_struct *conn,
|
|
struct dptr_struct *dirptr,
|
|
uint16_t flags2,
|
|
const char *path_mask,
|
|
uint32_t dirtype,
|
|
int info_level,
|
|
int requires_resume_key,
|
|
bool dont_descend,
|
|
bool ask_sharemode,
|
|
bool get_dosmode,
|
|
uint8_t align,
|
|
bool do_pad,
|
|
char **ppdata,
|
|
char *base_data,
|
|
char *end_data,
|
|
int space_remaining,
|
|
struct smb_filename **_smb_fname,
|
|
bool *got_exact_match,
|
|
int *_last_entry_off,
|
|
struct ea_list *name_list,
|
|
struct file_id *file_id)
|
|
{
|
|
const char *p;
|
|
const char *mask = NULL;
|
|
long prev_dirpos = 0;
|
|
uint32_t mode = 0;
|
|
char *fname = NULL;
|
|
struct smb_filename *smb_fname = NULL;
|
|
struct smbd_dirptr_lanman2_state state;
|
|
bool ok;
|
|
uint64_t last_entry_off = 0;
|
|
NTSTATUS status;
|
|
enum mangled_names_options mangled_names;
|
|
bool marshall_with_83_names;
|
|
|
|
mangled_names = lp_mangled_names(conn->params);
|
|
|
|
ZERO_STRUCT(state);
|
|
state.conn = conn;
|
|
state.info_level = info_level;
|
|
if (mangled_names != MANGLED_NAMES_NO) {
|
|
state.check_mangled_names = true;
|
|
}
|
|
state.has_wild = dptr_has_wild(dirptr);
|
|
state.got_exact_match = false;
|
|
state.case_sensitive = dptr_case_sensitive(dirptr);
|
|
|
|
*got_exact_match = false;
|
|
|
|
p = strrchr_m(path_mask,'/');
|
|
if(p != NULL) {
|
|
if(p[1] == '\0') {
|
|
mask = "*.*";
|
|
} else {
|
|
mask = p+1;
|
|
}
|
|
} else {
|
|
mask = path_mask;
|
|
}
|
|
|
|
ok = smbd_dirptr_get_entry(ctx,
|
|
dirptr,
|
|
mask,
|
|
dirtype,
|
|
dont_descend,
|
|
ask_sharemode,
|
|
get_dosmode,
|
|
smbd_dirptr_lanman2_match_fn,
|
|
smbd_dirptr_lanman2_mode_fn,
|
|
&state,
|
|
&fname,
|
|
&smb_fname,
|
|
&mode,
|
|
&prev_dirpos);
|
|
if (!ok) {
|
|
return NT_STATUS_END_OF_FILE;
|
|
}
|
|
|
|
*got_exact_match = state.got_exact_match;
|
|
|
|
marshall_with_83_names = (mangled_names == MANGLED_NAMES_YES);
|
|
|
|
status = smbd_marshall_dir_entry(ctx,
|
|
conn,
|
|
flags2,
|
|
info_level,
|
|
name_list,
|
|
marshall_with_83_names,
|
|
requires_resume_key,
|
|
mode,
|
|
fname,
|
|
smb_fname,
|
|
space_remaining,
|
|
align,
|
|
do_pad,
|
|
base_data,
|
|
ppdata,
|
|
end_data,
|
|
&last_entry_off);
|
|
if (NT_STATUS_EQUAL(status, NT_STATUS_ILLEGAL_CHARACTER)) {
|
|
DEBUG(1,("Conversion error: illegal character: %s\n",
|
|
smb_fname_str_dbg(smb_fname)));
|
|
}
|
|
|
|
if (file_id != NULL) {
|
|
*file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
|
|
}
|
|
|
|
if (!NT_STATUS_IS_OK(status) &&
|
|
!NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES))
|
|
{
|
|
TALLOC_FREE(smb_fname);
|
|
TALLOC_FREE(fname);
|
|
return status;
|
|
}
|
|
|
|
if (_smb_fname != NULL) {
|
|
/*
|
|
* smb_fname is already talloc'ed off ctx.
|
|
* We just need to make sure we don't return
|
|
* any stream_name, and replace base_name
|
|
* with fname in case base_name got mangled.
|
|
* This allows us to preserve any smb_fname->fsp
|
|
* for asynchronous handle lookups.
|
|
*/
|
|
TALLOC_FREE(smb_fname->stream_name);
|
|
TALLOC_FREE(smb_fname->base_name);
|
|
smb_fname->base_name = talloc_strdup(smb_fname, fname);
|
|
|
|
if (smb_fname->base_name == NULL) {
|
|
TALLOC_FREE(smb_fname);
|
|
TALLOC_FREE(fname);
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
*_smb_fname = smb_fname;
|
|
} else {
|
|
TALLOC_FREE(smb_fname);
|
|
}
|
|
TALLOC_FREE(fname);
|
|
|
|
if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
|
|
dptr_SeekDir(dirptr, prev_dirpos);
|
|
return status;
|
|
}
|
|
|
|
*_last_entry_off = last_entry_off;
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
unsigned char *create_volume_objectid(connection_struct *conn, unsigned char objid[16])
|
|
{
|
|
const struct loadparm_substitution *lp_sub =
|
|
loadparm_s3_global_substitution();
|
|
|
|
E_md4hash(lp_servicename(talloc_tos(), lp_sub, SNUM(conn)),objid);
|
|
return objid;
|
|
}
|
|
|
|
static void samba_extended_info_version(struct smb_extended_info *extended_info)
|
|
{
|
|
SMB_ASSERT(extended_info != NULL);
|
|
|
|
extended_info->samba_magic = SAMBA_EXTENDED_INFO_MAGIC;
|
|
extended_info->samba_version = ((SAMBA_VERSION_MAJOR & 0xff) << 24)
|
|
| ((SAMBA_VERSION_MINOR & 0xff) << 16)
|
|
| ((SAMBA_VERSION_RELEASE & 0xff) << 8);
|
|
#ifdef SAMBA_VERSION_REVISION
|
|
extended_info->samba_version |= (tolower(*SAMBA_VERSION_REVISION) - 'a' + 1) & 0xff;
|
|
#endif
|
|
extended_info->samba_subversion = 0;
|
|
#ifdef SAMBA_VERSION_RC_RELEASE
|
|
extended_info->samba_subversion |= (SAMBA_VERSION_RC_RELEASE & 0xff) << 24;
|
|
#else
|
|
#ifdef SAMBA_VERSION_PRE_RELEASE
|
|
extended_info->samba_subversion |= (SAMBA_VERSION_PRE_RELEASE & 0xff) << 16;
|
|
#endif
|
|
#endif
|
|
#ifdef SAMBA_VERSION_VENDOR_PATCH
|
|
extended_info->samba_subversion |= (SAMBA_VERSION_VENDOR_PATCH & 0xffff);
|
|
#endif
|
|
extended_info->samba_gitcommitdate = 0;
|
|
#ifdef SAMBA_VERSION_COMMIT_TIME
|
|
unix_to_nt_time(&extended_info->samba_gitcommitdate, SAMBA_VERSION_COMMIT_TIME);
|
|
#endif
|
|
|
|
memset(extended_info->samba_version_string, 0,
|
|
sizeof(extended_info->samba_version_string));
|
|
|
|
snprintf (extended_info->samba_version_string,
|
|
sizeof(extended_info->samba_version_string),
|
|
"%s", samba_version_string());
|
|
}
|
|
|
|
static bool fsinfo_unix_valid_level(connection_struct *conn,
|
|
uint16_t info_level)
|
|
{
|
|
if (conn->sconn->using_smb2 &&
|
|
lp_smb3_unix_extensions() &&
|
|
info_level == SMB2_FS_POSIX_INFORMATION_INTERNAL) {
|
|
return true;
|
|
}
|
|
#if defined(SMB1SERVER)
|
|
if (lp_smb1_unix_extensions() &&
|
|
info_level == SMB_QUERY_POSIX_FS_INFO) {
|
|
return true;
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
NTSTATUS smbd_do_qfsinfo(struct smbXsrv_connection *xconn,
|
|
connection_struct *conn,
|
|
TALLOC_CTX *mem_ctx,
|
|
uint16_t info_level,
|
|
uint16_t flags2,
|
|
unsigned int max_data_bytes,
|
|
size_t *fixed_portion,
|
|
struct smb_filename *fname,
|
|
char **ppdata,
|
|
int *ret_data_len)
|
|
{
|
|
const struct loadparm_substitution *lp_sub =
|
|
loadparm_s3_global_substitution();
|
|
char *pdata, *end_data;
|
|
int data_len = 0;
|
|
size_t len = 0;
|
|
const char *vname = volume_label(talloc_tos(), SNUM(conn));
|
|
int snum = SNUM(conn);
|
|
const char *fstype = lp_fstype(SNUM(conn));
|
|
const char *filename = NULL;
|
|
const uint64_t bytes_per_sector = 512;
|
|
uint32_t additional_flags = 0;
|
|
struct smb_filename smb_fname;
|
|
SMB_STRUCT_STAT st;
|
|
NTSTATUS status = NT_STATUS_OK;
|
|
uint64_t df_ret;
|
|
uint32_t serial;
|
|
|
|
if (fname == NULL || fname->base_name == NULL) {
|
|
filename = ".";
|
|
} else {
|
|
filename = fname->base_name;
|
|
}
|
|
|
|
if (IS_IPC(conn)) {
|
|
if (info_level != SMB_QUERY_CIFS_UNIX_INFO) {
|
|
DEBUG(0,("smbd_do_qfsinfo: not an allowed "
|
|
"info level (0x%x) on IPC$.\n",
|
|
(unsigned int)info_level));
|
|
return NT_STATUS_ACCESS_DENIED;
|
|
}
|
|
}
|
|
|
|
DEBUG(3,("smbd_do_qfsinfo: level = %d\n", info_level));
|
|
|
|
smb_fname = (struct smb_filename) {
|
|
.base_name = discard_const_p(char, filename),
|
|
.flags = fname ? fname->flags : 0,
|
|
.twrp = fname ? fname->twrp : 0,
|
|
};
|
|
|
|
if(info_level != SMB_FS_QUOTA_INFORMATION
|
|
&& SMB_VFS_STAT(conn, &smb_fname) != 0) {
|
|
DEBUG(2,("stat of . failed (%s)\n", strerror(errno)));
|
|
return map_nt_error_from_unix(errno);
|
|
}
|
|
|
|
st = smb_fname.st;
|
|
|
|
if (max_data_bytes + DIR_ENTRY_SAFETY_MARGIN < max_data_bytes) {
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
*ppdata = (char *)SMB_REALLOC(
|
|
*ppdata, max_data_bytes + DIR_ENTRY_SAFETY_MARGIN);
|
|
if (*ppdata == NULL) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
pdata = *ppdata;
|
|
memset((char *)pdata,'\0',max_data_bytes + DIR_ENTRY_SAFETY_MARGIN);
|
|
end_data = pdata + max_data_bytes + DIR_ENTRY_SAFETY_MARGIN - 1;
|
|
|
|
*fixed_portion = 0;
|
|
|
|
switch (info_level) {
|
|
case SMB_INFO_ALLOCATION:
|
|
{
|
|
uint64_t dfree,dsize,bsize,block_size,sectors_per_unit;
|
|
data_len = 18;
|
|
df_ret = get_dfree_info(conn, &smb_fname, &bsize,
|
|
&dfree, &dsize);
|
|
if (df_ret == (uint64_t)-1) {
|
|
return map_nt_error_from_unix(errno);
|
|
}
|
|
|
|
block_size = lp_block_size(snum);
|
|
if (bsize < block_size) {
|
|
uint64_t factor = block_size/bsize;
|
|
bsize = block_size;
|
|
dsize /= factor;
|
|
dfree /= factor;
|
|
}
|
|
if (bsize > block_size) {
|
|
uint64_t factor = bsize/block_size;
|
|
bsize = block_size;
|
|
dsize *= factor;
|
|
dfree *= factor;
|
|
}
|
|
sectors_per_unit = bsize/bytes_per_sector;
|
|
|
|
DEBUG(5,("smbd_do_qfsinfo : SMB_INFO_ALLOCATION id=%x, bsize=%u, cSectorUnit=%u, \
|
|
cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)st.st_ex_dev, (unsigned int)bsize, (unsigned int)sectors_per_unit,
|
|
(unsigned int)bytes_per_sector, (unsigned int)dsize, (unsigned int)dfree));
|
|
|
|
/*
|
|
* For large drives, return max values and not modulo.
|
|
*/
|
|
dsize = MIN(dsize, UINT32_MAX);
|
|
dfree = MIN(dfree, UINT32_MAX);
|
|
|
|
SIVAL(pdata,l1_idFileSystem,st.st_ex_dev);
|
|
SIVAL(pdata,l1_cSectorUnit,sectors_per_unit);
|
|
SIVAL(pdata,l1_cUnit,dsize);
|
|
SIVAL(pdata,l1_cUnitAvail,dfree);
|
|
SSVAL(pdata,l1_cbSector,bytes_per_sector);
|
|
break;
|
|
}
|
|
|
|
case SMB_INFO_VOLUME:
|
|
/* Return volume name */
|
|
/*
|
|
* Add volume serial number - hash of a combination of
|
|
* the called hostname and the service name.
|
|
*/
|
|
serial = generate_volume_serial_number(lp_sub, snum);
|
|
SIVAL(pdata,0,serial);
|
|
/*
|
|
* Win2k3 and previous mess this up by sending a name length
|
|
* one byte short. I believe only older clients (OS/2 Win9x) use
|
|
* this call so try fixing this by adding a terminating null to
|
|
* the pushed string. The change here was adding the STR_TERMINATE. JRA.
|
|
*/
|
|
status = srvstr_push(
|
|
pdata, flags2,
|
|
pdata+l2_vol_szVolLabel, vname,
|
|
PTR_DIFF(end_data, pdata+l2_vol_szVolLabel),
|
|
STR_NOALIGN|STR_TERMINATE, &len);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
SCVAL(pdata,l2_vol_cch,len);
|
|
data_len = l2_vol_szVolLabel + len;
|
|
DEBUG(5,("smbd_do_qfsinfo : time = %x, namelen = %u, "
|
|
"name = %s serial = 0x%04"PRIx32"\n",
|
|
(unsigned)convert_timespec_to_time_t(st.st_ex_ctime),
|
|
(unsigned)len, vname, serial));
|
|
break;
|
|
|
|
case SMB_QUERY_FS_ATTRIBUTE_INFO:
|
|
case SMB_FS_ATTRIBUTE_INFORMATION:
|
|
|
|
additional_flags = 0;
|
|
#if defined(HAVE_SYS_QUOTAS)
|
|
additional_flags |= FILE_VOLUME_QUOTAS;
|
|
#endif
|
|
|
|
if(lp_nt_acl_support(SNUM(conn))) {
|
|
additional_flags |= FILE_PERSISTENT_ACLS;
|
|
}
|
|
|
|
/* Capabilities are filled in at connection time through STATVFS call */
|
|
additional_flags |= conn->fs_capabilities;
|
|
additional_flags |= lp_parm_int(conn->params->service,
|
|
"share", "fake_fscaps",
|
|
0);
|
|
|
|
SIVAL(pdata,0,FILE_CASE_PRESERVED_NAMES|FILE_CASE_SENSITIVE_SEARCH|
|
|
FILE_SUPPORTS_OBJECT_IDS|FILE_UNICODE_ON_DISK|
|
|
additional_flags); /* FS ATTRIBUTES */
|
|
|
|
SIVAL(pdata,4,255); /* Max filename component length */
|
|
/* NOTE! the fstype must *not* be null terminated or win98 won't recognise it
|
|
and will think we can't do long filenames */
|
|
status = srvstr_push(pdata, flags2, pdata+12, fstype,
|
|
PTR_DIFF(end_data, pdata+12),
|
|
STR_UNICODE, &len);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
SIVAL(pdata,8,len);
|
|
data_len = 12 + len;
|
|
if (max_data_bytes >= 16 && data_len > max_data_bytes) {
|
|
/* the client only requested a portion of the
|
|
file system name */
|
|
data_len = max_data_bytes;
|
|
status = STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
*fixed_portion = 16;
|
|
break;
|
|
|
|
case SMB_QUERY_FS_LABEL_INFO:
|
|
case SMB_FS_LABEL_INFORMATION:
|
|
status = srvstr_push(pdata, flags2, pdata+4, vname,
|
|
PTR_DIFF(end_data, pdata+4), 0, &len);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
data_len = 4 + len;
|
|
SIVAL(pdata,0,len);
|
|
break;
|
|
|
|
case SMB_QUERY_FS_VOLUME_INFO:
|
|
case SMB_FS_VOLUME_INFORMATION:
|
|
put_long_date_full_timespec(TIMESTAMP_SET_NT_OR_BETTER,
|
|
pdata, &st.st_ex_btime);
|
|
/*
|
|
* Add volume serial number - hash of a combination of
|
|
* the called hostname and the service name.
|
|
*/
|
|
serial = generate_volume_serial_number(lp_sub, snum);
|
|
SIVAL(pdata,8,serial);
|
|
|
|
/* Max label len is 32 characters. */
|
|
status = srvstr_push(pdata, flags2, pdata+18, vname,
|
|
PTR_DIFF(end_data, pdata+18),
|
|
STR_UNICODE, &len);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
SIVAL(pdata,12,len);
|
|
data_len = 18+len;
|
|
|
|
DEBUG(5,("smbd_do_qfsinfo : SMB_QUERY_FS_VOLUME_INFO "
|
|
"namelen = %d, vol=%s serv=%s "
|
|
"serial=0x%04"PRIx32"\n",
|
|
(int)strlen(vname),vname,
|
|
lp_servicename(talloc_tos(), lp_sub, snum),
|
|
serial));
|
|
if (max_data_bytes >= 24 && data_len > max_data_bytes) {
|
|
/* the client only requested a portion of the
|
|
volume label */
|
|
data_len = max_data_bytes;
|
|
status = STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
*fixed_portion = 24;
|
|
break;
|
|
|
|
case SMB_QUERY_FS_SIZE_INFO:
|
|
case SMB_FS_SIZE_INFORMATION:
|
|
{
|
|
uint64_t dfree,dsize,bsize,block_size,sectors_per_unit;
|
|
data_len = 24;
|
|
df_ret = get_dfree_info(conn, &smb_fname, &bsize,
|
|
&dfree, &dsize);
|
|
if (df_ret == (uint64_t)-1) {
|
|
return map_nt_error_from_unix(errno);
|
|
}
|
|
block_size = lp_block_size(snum);
|
|
if (bsize < block_size) {
|
|
uint64_t factor = block_size/bsize;
|
|
bsize = block_size;
|
|
dsize /= factor;
|
|
dfree /= factor;
|
|
}
|
|
if (bsize > block_size) {
|
|
uint64_t factor = bsize/block_size;
|
|
bsize = block_size;
|
|
dsize *= factor;
|
|
dfree *= factor;
|
|
}
|
|
sectors_per_unit = bsize/bytes_per_sector;
|
|
DEBUG(5,("smbd_do_qfsinfo : SMB_QUERY_FS_SIZE_INFO bsize=%u, cSectorUnit=%u, \
|
|
cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned int)sectors_per_unit,
|
|
(unsigned int)bytes_per_sector, (unsigned int)dsize, (unsigned int)dfree));
|
|
SBIG_UINT(pdata,0,dsize);
|
|
SBIG_UINT(pdata,8,dfree);
|
|
SIVAL(pdata,16,sectors_per_unit);
|
|
SIVAL(pdata,20,bytes_per_sector);
|
|
*fixed_portion = 24;
|
|
break;
|
|
}
|
|
|
|
case SMB_FS_FULL_SIZE_INFORMATION:
|
|
{
|
|
uint64_t dfree,dsize,bsize,block_size,sectors_per_unit;
|
|
data_len = 32;
|
|
df_ret = get_dfree_info(conn, &smb_fname, &bsize,
|
|
&dfree, &dsize);
|
|
if (df_ret == (uint64_t)-1) {
|
|
return map_nt_error_from_unix(errno);
|
|
}
|
|
block_size = lp_block_size(snum);
|
|
if (bsize < block_size) {
|
|
uint64_t factor = block_size/bsize;
|
|
bsize = block_size;
|
|
dsize /= factor;
|
|
dfree /= factor;
|
|
}
|
|
if (bsize > block_size) {
|
|
uint64_t factor = bsize/block_size;
|
|
bsize = block_size;
|
|
dsize *= factor;
|
|
dfree *= factor;
|
|
}
|
|
sectors_per_unit = bsize/bytes_per_sector;
|
|
DEBUG(5,("smbd_do_qfsinfo : SMB_QUERY_FS_FULL_SIZE_INFO bsize=%u, cSectorUnit=%u, \
|
|
cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned int)sectors_per_unit,
|
|
(unsigned int)bytes_per_sector, (unsigned int)dsize, (unsigned int)dfree));
|
|
SBIG_UINT(pdata,0,dsize); /* Total Allocation units. */
|
|
SBIG_UINT(pdata,8,dfree); /* Caller available allocation units. */
|
|
SBIG_UINT(pdata,16,dfree); /* Actual available allocation units. */
|
|
SIVAL(pdata,24,sectors_per_unit); /* Sectors per allocation unit. */
|
|
SIVAL(pdata,28,bytes_per_sector); /* Bytes per sector. */
|
|
*fixed_portion = 32;
|
|
break;
|
|
}
|
|
|
|
case SMB_QUERY_FS_DEVICE_INFO:
|
|
case SMB_FS_DEVICE_INFORMATION:
|
|
{
|
|
uint32_t characteristics = FILE_DEVICE_IS_MOUNTED;
|
|
|
|
if (!CAN_WRITE(conn)) {
|
|
characteristics |= FILE_READ_ONLY_DEVICE;
|
|
}
|
|
data_len = 8;
|
|
SIVAL(pdata,0,FILE_DEVICE_DISK); /* dev type */
|
|
SIVAL(pdata,4,characteristics);
|
|
*fixed_portion = 8;
|
|
break;
|
|
}
|
|
|
|
#ifdef HAVE_SYS_QUOTAS
|
|
case SMB_FS_QUOTA_INFORMATION:
|
|
/*
|
|
* what we have to send --metze:
|
|
*
|
|
* Unknown1: 24 NULL bytes
|
|
* Soft Quota Treshold: 8 bytes seems like uint64_t or so
|
|
* Hard Quota Limit: 8 bytes seems like uint64_t or so
|
|
* Quota Flags: 2 byte :
|
|
* Unknown3: 6 NULL bytes
|
|
*
|
|
* 48 bytes total
|
|
*
|
|
* details for Quota Flags:
|
|
*
|
|
* 0x0020 Log Limit: log if the user exceeds his Hard Quota
|
|
* 0x0010 Log Warn: log if the user exceeds his Soft Quota
|
|
* 0x0002 Deny Disk: deny disk access when the user exceeds his Hard Quota
|
|
* 0x0001 Enable Quotas: enable quota for this fs
|
|
*
|
|
*/
|
|
{
|
|
/* we need to fake up a fsp here,
|
|
* because its not send in this call
|
|
*/
|
|
files_struct fsp;
|
|
SMB_NTQUOTA_STRUCT quotas;
|
|
|
|
ZERO_STRUCT(fsp);
|
|
ZERO_STRUCT(quotas);
|
|
|
|
fsp.conn = conn;
|
|
fsp.fnum = FNUM_FIELD_INVALID;
|
|
|
|
/* access check */
|
|
if (get_current_uid(conn) != 0) {
|
|
DEBUG(0,("get_user_quota: access_denied "
|
|
"service [%s] user [%s]\n",
|
|
lp_servicename(talloc_tos(), lp_sub, SNUM(conn)),
|
|
conn->session_info->unix_info->unix_name));
|
|
return NT_STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
status = vfs_get_ntquota(&fsp, SMB_USER_FS_QUOTA_TYPE,
|
|
NULL, "as);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
DEBUG(0,("vfs_get_ntquota() failed for service [%s]\n",lp_servicename(talloc_tos(), lp_sub, SNUM(conn))));
|
|
return status;
|
|
}
|
|
|
|
data_len = 48;
|
|
|
|
DEBUG(10,("SMB_FS_QUOTA_INFORMATION: for service [%s]\n",
|
|
lp_servicename(talloc_tos(), lp_sub, SNUM(conn))));
|
|
|
|
/* Unknown1 24 NULL bytes*/
|
|
SBIG_UINT(pdata,0,(uint64_t)0);
|
|
SBIG_UINT(pdata,8,(uint64_t)0);
|
|
SBIG_UINT(pdata,16,(uint64_t)0);
|
|
|
|
/* Default Soft Quota 8 bytes */
|
|
SBIG_UINT(pdata,24,quotas.softlim);
|
|
|
|
/* Default Hard Quota 8 bytes */
|
|
SBIG_UINT(pdata,32,quotas.hardlim);
|
|
|
|
/* Quota flag 2 bytes */
|
|
SSVAL(pdata,40,quotas.qflags);
|
|
|
|
/* Unknown3 6 NULL bytes */
|
|
SSVAL(pdata,42,0);
|
|
SIVAL(pdata,44,0);
|
|
|
|
break;
|
|
}
|
|
#endif /* HAVE_SYS_QUOTAS */
|
|
case SMB_FS_OBJECTID_INFORMATION:
|
|
{
|
|
unsigned char objid[16];
|
|
struct smb_extended_info extended_info;
|
|
memcpy(pdata,create_volume_objectid(conn, objid),16);
|
|
samba_extended_info_version (&extended_info);
|
|
SIVAL(pdata,16,extended_info.samba_magic);
|
|
SIVAL(pdata,20,extended_info.samba_version);
|
|
SIVAL(pdata,24,extended_info.samba_subversion);
|
|
SBIG_UINT(pdata,28,extended_info.samba_gitcommitdate);
|
|
memcpy(pdata+36,extended_info.samba_version_string,28);
|
|
data_len = 64;
|
|
break;
|
|
}
|
|
|
|
case SMB_FS_SECTOR_SIZE_INFORMATION:
|
|
{
|
|
data_len = 28;
|
|
/*
|
|
* These values match a physical Windows Server 2012
|
|
* share backed by NTFS atop spinning rust.
|
|
*/
|
|
DEBUG(5, ("SMB_FS_SECTOR_SIZE_INFORMATION:"));
|
|
/* logical_bytes_per_sector */
|
|
SIVAL(pdata, 0, bytes_per_sector);
|
|
/* phys_bytes_per_sector_atomic */
|
|
SIVAL(pdata, 4, bytes_per_sector);
|
|
/* phys_bytes_per_sector_perf */
|
|
SIVAL(pdata, 8, bytes_per_sector);
|
|
/* fs_effective_phys_bytes_per_sector_atomic */
|
|
SIVAL(pdata, 12, bytes_per_sector);
|
|
/* flags */
|
|
SIVAL(pdata, 16, SSINFO_FLAGS_ALIGNED_DEVICE
|
|
| SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE);
|
|
/* byte_off_sector_align */
|
|
SIVAL(pdata, 20, 0);
|
|
/* byte_off_partition_align */
|
|
SIVAL(pdata, 24, 0);
|
|
*fixed_portion = 28;
|
|
break;
|
|
}
|
|
|
|
|
|
#if defined(WITH_SMB1SERVER)
|
|
/*
|
|
* Query the version and capabilities of the CIFS UNIX extensions
|
|
* in use.
|
|
*/
|
|
|
|
case SMB_QUERY_CIFS_UNIX_INFO:
|
|
{
|
|
bool large_write = lp_min_receive_file_size() &&
|
|
!smb1_srv_is_signing_active(xconn);
|
|
bool large_read = !smb1_srv_is_signing_active(xconn);
|
|
int encrypt_caps = 0;
|
|
|
|
if (!lp_smb1_unix_extensions()) {
|
|
return NT_STATUS_INVALID_LEVEL;
|
|
}
|
|
|
|
switch (conn->encrypt_level) {
|
|
case SMB_SIGNING_OFF:
|
|
encrypt_caps = 0;
|
|
break;
|
|
case SMB_SIGNING_DESIRED:
|
|
case SMB_SIGNING_IF_REQUIRED:
|
|
case SMB_SIGNING_DEFAULT:
|
|
encrypt_caps = CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP;
|
|
break;
|
|
case SMB_SIGNING_REQUIRED:
|
|
encrypt_caps = CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP|
|
|
CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP;
|
|
large_write = false;
|
|
large_read = false;
|
|
break;
|
|
}
|
|
|
|
data_len = 12;
|
|
SSVAL(pdata,0,CIFS_UNIX_MAJOR_VERSION);
|
|
SSVAL(pdata,2,CIFS_UNIX_MINOR_VERSION);
|
|
|
|
/* We have POSIX ACLs, pathname, encryption,
|
|
* large read/write, and locking capability. */
|
|
|
|
SBIG_UINT(pdata,4,((uint64_t)(
|
|
CIFS_UNIX_POSIX_ACLS_CAP|
|
|
CIFS_UNIX_POSIX_PATHNAMES_CAP|
|
|
CIFS_UNIX_FCNTL_LOCKS_CAP|
|
|
CIFS_UNIX_EXTATTR_CAP|
|
|
CIFS_UNIX_POSIX_PATH_OPERATIONS_CAP|
|
|
encrypt_caps|
|
|
(large_read ? CIFS_UNIX_LARGE_READ_CAP : 0) |
|
|
(large_write ?
|
|
CIFS_UNIX_LARGE_WRITE_CAP : 0))));
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
case SMB_QUERY_POSIX_FS_INFO:
|
|
case SMB2_FS_POSIX_INFORMATION_INTERNAL:
|
|
{
|
|
int rc;
|
|
struct vfs_statvfs_struct svfs;
|
|
|
|
if (!fsinfo_unix_valid_level(conn, info_level)) {
|
|
return NT_STATUS_INVALID_LEVEL;
|
|
}
|
|
|
|
rc = SMB_VFS_STATVFS(conn, &smb_fname, &svfs);
|
|
|
|
if (!rc) {
|
|
data_len = 56;
|
|
SIVAL(pdata,0,svfs.OptimalTransferSize);
|
|
SIVAL(pdata,4,svfs.BlockSize);
|
|
SBIG_UINT(pdata,8,svfs.TotalBlocks);
|
|
SBIG_UINT(pdata,16,svfs.BlocksAvail);
|
|
SBIG_UINT(pdata,24,svfs.UserBlocksAvail);
|
|
SBIG_UINT(pdata,32,svfs.TotalFileNodes);
|
|
SBIG_UINT(pdata,40,svfs.FreeFileNodes);
|
|
SBIG_UINT(pdata,48,svfs.FsIdentifier);
|
|
DEBUG(5,("smbd_do_qfsinfo : SMB_QUERY_POSIX_FS_INFO succsessful\n"));
|
|
#ifdef EOPNOTSUPP
|
|
} else if (rc == EOPNOTSUPP) {
|
|
return NT_STATUS_INVALID_LEVEL;
|
|
#endif /* EOPNOTSUPP */
|
|
} else {
|
|
DEBUG(0,("vfs_statvfs() failed for service [%s]\n",lp_servicename(talloc_tos(), lp_sub, SNUM(conn))));
|
|
return NT_STATUS_DOS(ERRSRV, ERRerror);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SMB_QUERY_POSIX_WHOAMI:
|
|
{
|
|
uint32_t flags = 0;
|
|
uint32_t sid_bytes;
|
|
uint32_t i;
|
|
|
|
if (!lp_smb1_unix_extensions()) {
|
|
return NT_STATUS_INVALID_LEVEL;
|
|
}
|
|
|
|
if (max_data_bytes < 40) {
|
|
return NT_STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
if (security_session_user_level(conn->session_info, NULL) < SECURITY_USER) {
|
|
flags |= SMB_WHOAMI_GUEST;
|
|
}
|
|
|
|
/* NOTE: 8 bytes for UID/GID, irrespective of native
|
|
* platform size. This matches
|
|
* SMB_QUERY_FILE_UNIX_BASIC and friends.
|
|
*/
|
|
data_len = 4 /* flags */
|
|
+ 4 /* flag mask */
|
|
+ 8 /* uid */
|
|
+ 8 /* gid */
|
|
+ 4 /* ngroups */
|
|
+ 4 /* num_sids */
|
|
+ 4 /* SID bytes */
|
|
+ 4 /* pad/reserved */
|
|
+ (conn->session_info->unix_token->ngroups * 8)
|
|
/* groups list */
|
|
+ (conn->session_info->security_token->num_sids *
|
|
SID_MAX_SIZE)
|
|
/* SID list */;
|
|
|
|
SIVAL(pdata, 0, flags);
|
|
SIVAL(pdata, 4, SMB_WHOAMI_MASK);
|
|
SBIG_UINT(pdata, 8,
|
|
(uint64_t)conn->session_info->unix_token->uid);
|
|
SBIG_UINT(pdata, 16,
|
|
(uint64_t)conn->session_info->unix_token->gid);
|
|
|
|
|
|
if (data_len >= max_data_bytes) {
|
|
/* Potential overflow, skip the GIDs and SIDs. */
|
|
|
|
SIVAL(pdata, 24, 0); /* num_groups */
|
|
SIVAL(pdata, 28, 0); /* num_sids */
|
|
SIVAL(pdata, 32, 0); /* num_sid_bytes */
|
|
SIVAL(pdata, 36, 0); /* reserved */
|
|
|
|
data_len = 40;
|
|
break;
|
|
}
|
|
|
|
SIVAL(pdata, 24, conn->session_info->unix_token->ngroups);
|
|
SIVAL(pdata, 28, conn->session_info->security_token->num_sids);
|
|
|
|
/* We walk the SID list twice, but this call is fairly
|
|
* infrequent, and I don't expect that it's performance
|
|
* sensitive -- jpeach
|
|
*/
|
|
for (i = 0, sid_bytes = 0;
|
|
i < conn->session_info->security_token->num_sids; ++i) {
|
|
sid_bytes += ndr_size_dom_sid(
|
|
&conn->session_info->security_token->sids[i],
|
|
0);
|
|
}
|
|
|
|
/* SID list byte count */
|
|
SIVAL(pdata, 32, sid_bytes);
|
|
|
|
/* 4 bytes pad/reserved - must be zero */
|
|
SIVAL(pdata, 36, 0);
|
|
data_len = 40;
|
|
|
|
/* GID list */
|
|
for (i = 0; i < conn->session_info->unix_token->ngroups; ++i) {
|
|
SBIG_UINT(pdata, data_len,
|
|
(uint64_t)conn->session_info->unix_token->groups[i]);
|
|
data_len += 8;
|
|
}
|
|
|
|
/* SID list */
|
|
for (i = 0;
|
|
i < conn->session_info->security_token->num_sids; ++i) {
|
|
int sid_len = ndr_size_dom_sid(
|
|
&conn->session_info->security_token->sids[i],
|
|
0);
|
|
|
|
sid_linearize((uint8_t *)(pdata + data_len),
|
|
sid_len,
|
|
&conn->session_info->security_token->sids[i]);
|
|
data_len += sid_len;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case SMB_MAC_QUERY_FS_INFO:
|
|
/*
|
|
* Thursby MAC extension... ONLY on NTFS filesystems
|
|
* once we do streams then we don't need this
|
|
*/
|
|
if (strequal(lp_fstype(SNUM(conn)),"NTFS")) {
|
|
data_len = 88;
|
|
SIVAL(pdata,84,0x100); /* Don't support mac... */
|
|
break;
|
|
}
|
|
|
|
FALL_THROUGH;
|
|
default:
|
|
return NT_STATUS_INVALID_LEVEL;
|
|
}
|
|
|
|
*ret_data_len = data_len;
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS smb_set_fsquota(connection_struct *conn,
|
|
struct smb_request *req,
|
|
files_struct *fsp,
|
|
const DATA_BLOB *qdata)
|
|
{
|
|
const struct loadparm_substitution *lp_sub =
|
|
loadparm_s3_global_substitution();
|
|
NTSTATUS status;
|
|
SMB_NTQUOTA_STRUCT quotas;
|
|
|
|
ZERO_STRUCT(quotas);
|
|
|
|
/* access check */
|
|
if ((get_current_uid(conn) != 0) || !CAN_WRITE(conn)) {
|
|
DEBUG(3, ("set_fsquota: access_denied service [%s] user [%s]\n",
|
|
lp_servicename(talloc_tos(), lp_sub, SNUM(conn)),
|
|
conn->session_info->unix_info->unix_name));
|
|
return NT_STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
if (!check_fsp_ntquota_handle(conn, req,
|
|
fsp)) {
|
|
DEBUG(1, ("set_fsquota: no valid QUOTA HANDLE\n"));
|
|
return NT_STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
/* note: normally there're 48 bytes,
|
|
* but we didn't use the last 6 bytes for now
|
|
* --metze
|
|
*/
|
|
if (qdata->length < 42) {
|
|
DEBUG(0,("set_fsquota: requires total_data(%u) >= 42 bytes!\n",
|
|
(unsigned int)qdata->length));
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* unknown_1 24 NULL bytes in pdata*/
|
|
|
|
/* the soft quotas 8 bytes (uint64_t)*/
|
|
quotas.softlim = BVAL(qdata->data,24);
|
|
|
|
/* the hard quotas 8 bytes (uint64_t)*/
|
|
quotas.hardlim = BVAL(qdata->data,32);
|
|
|
|
/* quota_flags 2 bytes **/
|
|
quotas.qflags = SVAL(qdata->data,40);
|
|
|
|
/* unknown_2 6 NULL bytes follow*/
|
|
|
|
/* now set the quotas */
|
|
if (vfs_set_ntquota(fsp, SMB_USER_FS_QUOTA_TYPE, NULL, "as)!=0) {
|
|
DEBUG(1, ("vfs_set_ntquota() failed for service [%s]\n",
|
|
lp_servicename(talloc_tos(), lp_sub, SNUM(conn))));
|
|
status = map_nt_error_from_unix(errno);
|
|
} else {
|
|
status = NT_STATUS_OK;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS smbd_do_setfsinfo(connection_struct *conn,
|
|
struct smb_request *req,
|
|
TALLOC_CTX *mem_ctx,
|
|
uint16_t info_level,
|
|
files_struct *fsp,
|
|
const DATA_BLOB *pdata)
|
|
{
|
|
switch (info_level) {
|
|
case SMB_FS_QUOTA_INFORMATION:
|
|
{
|
|
return smb_set_fsquota(conn,
|
|
req,
|
|
fsp,
|
|
pdata);
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return NT_STATUS_INVALID_LEVEL;
|
|
}
|
|
|
|
#if defined(HAVE_POSIX_ACLS)
|
|
/****************************************************************************
|
|
Utility function to count the number of entries in a POSIX acl.
|
|
****************************************************************************/
|
|
|
|
static unsigned int count_acl_entries(connection_struct *conn, SMB_ACL_T posix_acl)
|
|
{
|
|
unsigned int ace_count = 0;
|
|
int entry_id = SMB_ACL_FIRST_ENTRY;
|
|
SMB_ACL_ENTRY_T entry;
|
|
|
|
while ( posix_acl && (sys_acl_get_entry(posix_acl, entry_id, &entry) == 1)) {
|
|
/* get_next... */
|
|
if (entry_id == SMB_ACL_FIRST_ENTRY) {
|
|
entry_id = SMB_ACL_NEXT_ENTRY;
|
|
}
|
|
ace_count++;
|
|
}
|
|
return ace_count;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Utility function to marshall a POSIX acl into wire format.
|
|
****************************************************************************/
|
|
|
|
static bool marshall_posix_acl(connection_struct *conn, char *pdata, SMB_STRUCT_STAT *pst, SMB_ACL_T posix_acl)
|
|
{
|
|
int entry_id = SMB_ACL_FIRST_ENTRY;
|
|
SMB_ACL_ENTRY_T entry;
|
|
|
|
while ( posix_acl && (sys_acl_get_entry(posix_acl, entry_id, &entry) == 1)) {
|
|
SMB_ACL_TAG_T tagtype;
|
|
SMB_ACL_PERMSET_T permset;
|
|
unsigned char perms = 0;
|
|
unsigned int own_grp;
|
|
|
|
/* get_next... */
|
|
if (entry_id == SMB_ACL_FIRST_ENTRY) {
|
|
entry_id = SMB_ACL_NEXT_ENTRY;
|
|
}
|
|
|
|
if (sys_acl_get_tag_type(entry, &tagtype) == -1) {
|
|
DEBUG(0,("marshall_posix_acl: SMB_VFS_SYS_ACL_GET_TAG_TYPE failed.\n"));
|
|
return False;
|
|
}
|
|
|
|
if (sys_acl_get_permset(entry, &permset) == -1) {
|
|
DEBUG(0,("marshall_posix_acl: SMB_VFS_SYS_ACL_GET_PERMSET failed.\n"));
|
|
return False;
|
|
}
|
|
|
|
perms |= (sys_acl_get_perm(permset, SMB_ACL_READ) ? SMB_POSIX_ACL_READ : 0);
|
|
perms |= (sys_acl_get_perm(permset, SMB_ACL_WRITE) ? SMB_POSIX_ACL_WRITE : 0);
|
|
perms |= (sys_acl_get_perm(permset, SMB_ACL_EXECUTE) ? SMB_POSIX_ACL_EXECUTE : 0);
|
|
|
|
SCVAL(pdata,1,perms);
|
|
|
|
switch (tagtype) {
|
|
case SMB_ACL_USER_OBJ:
|
|
SCVAL(pdata,0,SMB_POSIX_ACL_USER_OBJ);
|
|
own_grp = (unsigned int)pst->st_ex_uid;
|
|
SIVAL(pdata,2,own_grp);
|
|
SIVAL(pdata,6,0);
|
|
break;
|
|
case SMB_ACL_USER:
|
|
{
|
|
uid_t *puid = (uid_t *)sys_acl_get_qualifier(entry);
|
|
if (!puid) {
|
|
DEBUG(0,("marshall_posix_acl: SMB_VFS_SYS_ACL_GET_QUALIFIER failed.\n"));
|
|
return False;
|
|
}
|
|
own_grp = (unsigned int)*puid;
|
|
SCVAL(pdata,0,SMB_POSIX_ACL_USER);
|
|
SIVAL(pdata,2,own_grp);
|
|
SIVAL(pdata,6,0);
|
|
break;
|
|
}
|
|
case SMB_ACL_GROUP_OBJ:
|
|
SCVAL(pdata,0,SMB_POSIX_ACL_GROUP_OBJ);
|
|
own_grp = (unsigned int)pst->st_ex_gid;
|
|
SIVAL(pdata,2,own_grp);
|
|
SIVAL(pdata,6,0);
|
|
break;
|
|
case SMB_ACL_GROUP:
|
|
{
|
|
gid_t *pgid= (gid_t *)sys_acl_get_qualifier(entry);
|
|
if (!pgid) {
|
|
DEBUG(0,("marshall_posix_acl: SMB_VFS_SYS_ACL_GET_QUALIFIER failed.\n"));
|
|
return False;
|
|
}
|
|
own_grp = (unsigned int)*pgid;
|
|
SCVAL(pdata,0,SMB_POSIX_ACL_GROUP);
|
|
SIVAL(pdata,2,own_grp);
|
|
SIVAL(pdata,6,0);
|
|
break;
|
|
}
|
|
case SMB_ACL_MASK:
|
|
SCVAL(pdata,0,SMB_POSIX_ACL_MASK);
|
|
SIVAL(pdata,2,0xFFFFFFFF);
|
|
SIVAL(pdata,6,0xFFFFFFFF);
|
|
break;
|
|
case SMB_ACL_OTHER:
|
|
SCVAL(pdata,0,SMB_POSIX_ACL_OTHER);
|
|
SIVAL(pdata,2,0xFFFFFFFF);
|
|
SIVAL(pdata,6,0xFFFFFFFF);
|
|
break;
|
|
default:
|
|
DEBUG(0,("marshall_posix_acl: unknown tagtype.\n"));
|
|
return False;
|
|
}
|
|
pdata += SMB_POSIX_ACL_ENTRY_SIZE;
|
|
}
|
|
|
|
return True;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
Store the FILE_UNIX_BASIC info.
|
|
****************************************************************************/
|
|
|
|
char *store_file_unix_basic(connection_struct *conn,
|
|
char *pdata,
|
|
files_struct *fsp,
|
|
const SMB_STRUCT_STAT *psbuf)
|
|
{
|
|
dev_t devno;
|
|
|
|
DEBUG(10,("store_file_unix_basic: SMB_QUERY_FILE_UNIX_BASIC\n"));
|
|
DEBUG(4,("store_file_unix_basic: st_mode=%o\n",(int)psbuf->st_ex_mode));
|
|
|
|
SOFF_T(pdata,0,get_file_size_stat(psbuf)); /* File size 64 Bit */
|
|
pdata += 8;
|
|
|
|
SOFF_T(pdata,0,SMB_VFS_GET_ALLOC_SIZE(conn,fsp,psbuf)); /* Number of bytes used on disk - 64 Bit */
|
|
pdata += 8;
|
|
|
|
put_long_date_full_timespec(TIMESTAMP_SET_NT_OR_BETTER, pdata, &psbuf->st_ex_ctime); /* Change Time 64 Bit */
|
|
put_long_date_full_timespec(TIMESTAMP_SET_NT_OR_BETTER ,pdata+8, &psbuf->st_ex_atime); /* Last access time 64 Bit */
|
|
put_long_date_full_timespec(TIMESTAMP_SET_NT_OR_BETTER, pdata+16, &psbuf->st_ex_mtime); /* Last modification time 64 Bit */
|
|
pdata += 24;
|
|
|
|
SIVAL(pdata,0,psbuf->st_ex_uid); /* user id for the owner */
|
|
SIVAL(pdata,4,0);
|
|
pdata += 8;
|
|
|
|
SIVAL(pdata,0,psbuf->st_ex_gid); /* group id of owner */
|
|
SIVAL(pdata,4,0);
|
|
pdata += 8;
|
|
|
|
SIVAL(pdata,0,unix_filetype(psbuf->st_ex_mode));
|
|
pdata += 4;
|
|
|
|
if (S_ISBLK(psbuf->st_ex_mode) || S_ISCHR(psbuf->st_ex_mode)) {
|
|
devno = psbuf->st_ex_rdev;
|
|
} else {
|
|
devno = psbuf->st_ex_dev;
|
|
}
|
|
|
|
SIVAL(pdata,0,unix_dev_major(devno)); /* Major device number if type is device */
|
|
SIVAL(pdata,4,0);
|
|
pdata += 8;
|
|
|
|
SIVAL(pdata,0,unix_dev_minor(devno)); /* Minor device number if type is device */
|
|
SIVAL(pdata,4,0);
|
|
pdata += 8;
|
|
|
|
SINO_T_VAL(pdata, 0, psbuf->st_ex_ino); /* inode number */
|
|
pdata += 8;
|
|
|
|
SIVAL(pdata,0, unix_perms_to_wire(psbuf->st_ex_mode)); /* Standard UNIX file permissions */
|
|
SIVAL(pdata,4,0);
|
|
pdata += 8;
|
|
|
|
SIVAL(pdata,0,psbuf->st_ex_nlink); /* number of hard links */
|
|
SIVAL(pdata,4,0);
|
|
pdata += 8;
|
|
|
|
return pdata;
|
|
}
|
|
|
|
/* Forward and reverse mappings from the UNIX_INFO2 file flags field and
|
|
* the chflags(2) (or equivalent) flags.
|
|
*
|
|
* XXX: this really should be behind the VFS interface. To do this, we would
|
|
* need to alter SMB_STRUCT_STAT so that it included a flags and a mask field.
|
|
* Each VFS module could then implement its own mapping as appropriate for the
|
|
* platform. We would then pass the SMB flags into SMB_VFS_CHFLAGS.
|
|
*/
|
|
static const struct {unsigned stat_fflag; unsigned smb_fflag;}
|
|
info2_flags_map[] =
|
|
{
|
|
#ifdef UF_NODUMP
|
|
{ UF_NODUMP, EXT_DO_NOT_BACKUP },
|
|
#endif
|
|
|
|
#ifdef UF_IMMUTABLE
|
|
{ UF_IMMUTABLE, EXT_IMMUTABLE },
|
|
#endif
|
|
|
|
#ifdef UF_APPEND
|
|
{ UF_APPEND, EXT_OPEN_APPEND_ONLY },
|
|
#endif
|
|
|
|
#ifdef UF_HIDDEN
|
|
{ UF_HIDDEN, EXT_HIDDEN },
|
|
#endif
|
|
|
|
/* Do not remove. We need to guarantee that this array has at least one
|
|
* entry to build on HP-UX.
|
|
*/
|
|
{ 0, 0 }
|
|
|
|
};
|
|
|
|
static void map_info2_flags_from_sbuf(const SMB_STRUCT_STAT *psbuf,
|
|
uint32_t *smb_fflags, uint32_t *smb_fmask)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(info2_flags_map); ++i) {
|
|
*smb_fmask |= info2_flags_map[i].smb_fflag;
|
|
if (psbuf->st_ex_flags & info2_flags_map[i].stat_fflag) {
|
|
*smb_fflags |= info2_flags_map[i].smb_fflag;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool map_info2_flags_to_sbuf(const SMB_STRUCT_STAT *psbuf,
|
|
const uint32_t smb_fflags,
|
|
const uint32_t smb_fmask,
|
|
int *stat_fflags)
|
|
{
|
|
uint32_t max_fmask = 0;
|
|
size_t i;
|
|
|
|
*stat_fflags = psbuf->st_ex_flags;
|
|
|
|
/* For each flags requested in smb_fmask, check the state of the
|
|
* corresponding flag in smb_fflags and set or clear the matching
|
|
* stat flag.
|
|
*/
|
|
|
|
for (i = 0; i < ARRAY_SIZE(info2_flags_map); ++i) {
|
|
max_fmask |= info2_flags_map[i].smb_fflag;
|
|
if (smb_fmask & info2_flags_map[i].smb_fflag) {
|
|
if (smb_fflags & info2_flags_map[i].smb_fflag) {
|
|
*stat_fflags |= info2_flags_map[i].stat_fflag;
|
|
} else {
|
|
*stat_fflags &= ~info2_flags_map[i].stat_fflag;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* If smb_fmask is asking to set any bits that are not supported by
|
|
* our flag mappings, we should fail.
|
|
*/
|
|
if ((smb_fmask & max_fmask) != smb_fmask) {
|
|
return False;
|
|
}
|
|
|
|
return True;
|
|
}
|
|
|
|
|
|
/* Just like SMB_QUERY_FILE_UNIX_BASIC, but with the addition
|
|
* of file flags and birth (create) time.
|
|
*/
|
|
char *store_file_unix_basic_info2(connection_struct *conn,
|
|
char *pdata,
|
|
files_struct *fsp,
|
|
const SMB_STRUCT_STAT *psbuf)
|
|
{
|
|
uint32_t file_flags = 0;
|
|
uint32_t flags_mask = 0;
|
|
|
|
pdata = store_file_unix_basic(conn, pdata, fsp, psbuf);
|
|
|
|
/* Create (birth) time 64 bit */
|
|
put_long_date_full_timespec(TIMESTAMP_SET_NT_OR_BETTER,pdata, &psbuf->st_ex_btime);
|
|
pdata += 8;
|
|
|
|
map_info2_flags_from_sbuf(psbuf, &file_flags, &flags_mask);
|
|
SIVAL(pdata, 0, file_flags); /* flags */
|
|
SIVAL(pdata, 4, flags_mask); /* mask */
|
|
pdata += 8;
|
|
|
|
return pdata;
|
|
}
|
|
|
|
static NTSTATUS marshall_stream_info(unsigned int num_streams,
|
|
const struct stream_struct *streams,
|
|
char *data,
|
|
unsigned int max_data_bytes,
|
|
unsigned int *data_size)
|
|
{
|
|
unsigned int i;
|
|
unsigned int ofs = 0;
|
|
|
|
if (max_data_bytes < 32) {
|
|
return NT_STATUS_INFO_LENGTH_MISMATCH;
|
|
}
|
|
|
|
for (i = 0; i < num_streams; i++) {
|
|
unsigned int next_offset;
|
|
size_t namelen;
|
|
smb_ucs2_t *namebuf;
|
|
|
|
if (!push_ucs2_talloc(talloc_tos(), &namebuf,
|
|
streams[i].name, &namelen) ||
|
|
namelen <= 2)
|
|
{
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/*
|
|
* name_buf is now null-terminated, we need to marshall as not
|
|
* terminated
|
|
*/
|
|
|
|
namelen -= 2;
|
|
|
|
/*
|
|
* We cannot overflow ...
|
|
*/
|
|
if ((ofs + 24 + namelen) > max_data_bytes) {
|
|
DEBUG(10, ("refusing to overflow reply at stream %u\n",
|
|
i));
|
|
TALLOC_FREE(namebuf);
|
|
return STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
|
|
SIVAL(data, ofs+4, namelen);
|
|
SOFF_T(data, ofs+8, streams[i].size);
|
|
SOFF_T(data, ofs+16, streams[i].alloc_size);
|
|
memcpy(data+ofs+24, namebuf, namelen);
|
|
TALLOC_FREE(namebuf);
|
|
|
|
next_offset = ofs + 24 + namelen;
|
|
|
|
if (i == num_streams-1) {
|
|
SIVAL(data, ofs, 0);
|
|
}
|
|
else {
|
|
unsigned int align = ndr_align_size(next_offset, 8);
|
|
|
|
if ((next_offset + align) > max_data_bytes) {
|
|
DEBUG(10, ("refusing to overflow align "
|
|
"reply at stream %u\n",
|
|
i));
|
|
TALLOC_FREE(namebuf);
|
|
return STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
|
|
memset(data+next_offset, 0, align);
|
|
next_offset += align;
|
|
|
|
SIVAL(data, ofs, next_offset - ofs);
|
|
ofs = next_offset;
|
|
}
|
|
|
|
ofs = next_offset;
|
|
}
|
|
|
|
DEBUG(10, ("max_data: %u, data_size: %u\n", max_data_bytes, ofs));
|
|
|
|
*data_size = ofs;
|
|
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
static NTSTATUS smb_unix_read_symlink(connection_struct *conn,
|
|
struct smb_request *req,
|
|
struct smb_filename *smb_fname,
|
|
char *pdata,
|
|
unsigned int data_size_in,
|
|
unsigned int *pdata_size_out)
|
|
{
|
|
NTSTATUS status;
|
|
size_t len = 0;
|
|
int link_len = 0;
|
|
struct smb_filename *parent_fname = NULL;
|
|
struct smb_filename *base_name = NULL;
|
|
|
|
char *buffer = talloc_array(talloc_tos(), char, PATH_MAX+1);
|
|
|
|
if (!buffer) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
DBG_DEBUG("SMB_QUERY_FILE_UNIX_LINK for file %s\n",
|
|
smb_fname_str_dbg(smb_fname));
|
|
|
|
if(!S_ISLNK(smb_fname->st.st_ex_mode)) {
|
|
TALLOC_FREE(buffer);
|
|
return NT_STATUS_DOS(ERRSRV, ERRbadlink);
|
|
}
|
|
|
|
status = parent_pathref(talloc_tos(),
|
|
conn->cwd_fsp,
|
|
smb_fname,
|
|
&parent_fname,
|
|
&base_name);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
TALLOC_FREE(buffer);
|
|
return status;
|
|
}
|
|
|
|
link_len = SMB_VFS_READLINKAT(conn,
|
|
parent_fname->fsp,
|
|
base_name,
|
|
buffer,
|
|
PATH_MAX);
|
|
|
|
TALLOC_FREE(parent_fname);
|
|
|
|
if (link_len == -1) {
|
|
TALLOC_FREE(buffer);
|
|
return map_nt_error_from_unix(errno);
|
|
}
|
|
|
|
buffer[link_len] = 0;
|
|
status = srvstr_push(pdata,
|
|
req->flags2,
|
|
pdata,
|
|
buffer,
|
|
data_size_in,
|
|
STR_TERMINATE,
|
|
&len);
|
|
TALLOC_FREE(buffer);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
*pdata_size_out = len;
|
|
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
#if defined(HAVE_POSIX_ACLS)
|
|
static NTSTATUS smb_query_posix_acl(connection_struct *conn,
|
|
struct smb_request *req,
|
|
files_struct *fsp,
|
|
struct smb_filename *smb_fname,
|
|
char *pdata,
|
|
unsigned int data_size_in,
|
|
unsigned int *pdata_size_out)
|
|
{
|
|
SMB_ACL_T file_acl = NULL;
|
|
SMB_ACL_T def_acl = NULL;
|
|
uint16_t num_file_acls = 0;
|
|
uint16_t num_def_acls = 0;
|
|
unsigned int size_needed = 0;
|
|
NTSTATUS status;
|
|
bool ok;
|
|
bool close_fsp = false;
|
|
|
|
/*
|
|
* Ensure we always operate on a file descriptor, not just
|
|
* the filename.
|
|
*/
|
|
if (fsp == NULL || !fsp->fsp_flags.is_fsa) {
|
|
uint32_t access_mask = SEC_STD_READ_CONTROL|
|
|
FILE_READ_ATTRIBUTES|
|
|
FILE_WRITE_ATTRIBUTES;
|
|
|
|
status = get_posix_fsp(conn,
|
|
req,
|
|
smb_fname,
|
|
access_mask,
|
|
&fsp);
|
|
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
goto out;
|
|
}
|
|
close_fsp = true;
|
|
}
|
|
|
|
SMB_ASSERT(fsp != NULL);
|
|
|
|
status = refuse_symlink_fsp(fsp);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
goto out;
|
|
}
|
|
|
|
file_acl = SMB_VFS_SYS_ACL_GET_FD(fsp, SMB_ACL_TYPE_ACCESS,
|
|
talloc_tos());
|
|
|
|
if (file_acl == NULL && no_acl_syscall_error(errno)) {
|
|
DBG_INFO("ACLs not implemented on "
|
|
"filesystem containing %s\n",
|
|
fsp_str_dbg(fsp));
|
|
status = NT_STATUS_NOT_IMPLEMENTED;
|
|
goto out;
|
|
}
|
|
|
|
if (S_ISDIR(fsp->fsp_name->st.st_ex_mode)) {
|
|
/*
|
|
* We can only have default POSIX ACLs on
|
|
* directories.
|
|
*/
|
|
if (!fsp->fsp_flags.is_directory) {
|
|
DBG_INFO("Non-directory open %s\n",
|
|
fsp_str_dbg(fsp));
|
|
status = NT_STATUS_INVALID_HANDLE;
|
|
goto out;
|
|
}
|
|
def_acl = SMB_VFS_SYS_ACL_GET_FD(fsp,
|
|
SMB_ACL_TYPE_DEFAULT,
|
|
talloc_tos());
|
|
def_acl = free_empty_sys_acl(conn, def_acl);
|
|
}
|
|
|
|
num_file_acls = count_acl_entries(conn, file_acl);
|
|
num_def_acls = count_acl_entries(conn, def_acl);
|
|
|
|
/* Wrap checks. */
|
|
if (num_file_acls + num_def_acls < num_file_acls) {
|
|
status = NT_STATUS_INVALID_PARAMETER;
|
|
goto out;
|
|
}
|
|
|
|
size_needed = num_file_acls + num_def_acls;
|
|
|
|
/*
|
|
* (size_needed * SMB_POSIX_ACL_ENTRY_SIZE) must be less
|
|
* than UINT_MAX, so check by division.
|
|
*/
|
|
if (size_needed > (UINT_MAX/SMB_POSIX_ACL_ENTRY_SIZE)) {
|
|
status = NT_STATUS_INVALID_PARAMETER;
|
|
goto out;
|
|
}
|
|
|
|
size_needed = size_needed*SMB_POSIX_ACL_ENTRY_SIZE;
|
|
if (size_needed + SMB_POSIX_ACL_HEADER_SIZE < size_needed) {
|
|
status = NT_STATUS_INVALID_PARAMETER;
|
|
goto out;
|
|
}
|
|
size_needed += SMB_POSIX_ACL_HEADER_SIZE;
|
|
|
|
if ( data_size_in < size_needed) {
|
|
DBG_INFO("data_size too small (%u) need %u\n",
|
|
data_size_in,
|
|
size_needed);
|
|
status = NT_STATUS_BUFFER_TOO_SMALL;
|
|
goto out;
|
|
}
|
|
|
|
SSVAL(pdata,0,SMB_POSIX_ACL_VERSION);
|
|
SSVAL(pdata,2,num_file_acls);
|
|
SSVAL(pdata,4,num_def_acls);
|
|
pdata += SMB_POSIX_ACL_HEADER_SIZE;
|
|
|
|
ok = marshall_posix_acl(conn,
|
|
pdata,
|
|
&fsp->fsp_name->st,
|
|
file_acl);
|
|
if (!ok) {
|
|
status = NT_STATUS_INTERNAL_ERROR;
|
|
goto out;
|
|
}
|
|
pdata += (num_file_acls*SMB_POSIX_ACL_ENTRY_SIZE);
|
|
|
|
ok = marshall_posix_acl(conn,
|
|
pdata,
|
|
&fsp->fsp_name->st,
|
|
def_acl);
|
|
if (!ok) {
|
|
status = NT_STATUS_INTERNAL_ERROR;
|
|
goto out;
|
|
}
|
|
|
|
*pdata_size_out = size_needed;
|
|
status = NT_STATUS_OK;
|
|
|
|
out:
|
|
|
|
if (close_fsp) {
|
|
/*
|
|
* Ensure the stat struct in smb_fname is up to
|
|
* date. Structure copy.
|
|
*/
|
|
smb_fname->st = fsp->fsp_name->st;
|
|
(void)close_file_free(req, &fsp, NORMAL_CLOSE);
|
|
}
|
|
|
|
TALLOC_FREE(file_acl);
|
|
TALLOC_FREE(def_acl);
|
|
return status;
|
|
}
|
|
#endif
|
|
|
|
NTSTATUS smbd_do_qfilepathinfo(connection_struct *conn,
|
|
TALLOC_CTX *mem_ctx,
|
|
struct smb_request *req,
|
|
uint16_t info_level,
|
|
files_struct *fsp,
|
|
struct smb_filename *smb_fname,
|
|
bool delete_pending,
|
|
struct timespec write_time_ts,
|
|
struct ea_list *ea_list,
|
|
uint16_t flags2,
|
|
unsigned int max_data_bytes,
|
|
size_t *fixed_portion,
|
|
char **ppdata,
|
|
unsigned int *pdata_size)
|
|
{
|
|
char *pdata = *ppdata;
|
|
char *dstart, *dend;
|
|
unsigned int data_size;
|
|
struct timespec create_time_ts, mtime_ts, atime_ts, ctime_ts;
|
|
time_t create_time, mtime, atime, c_time;
|
|
SMB_STRUCT_STAT *psbuf = NULL;
|
|
SMB_STRUCT_STAT *base_sp = NULL;
|
|
char *p;
|
|
char *base_name;
|
|
char *dos_fname;
|
|
int mode;
|
|
int nlink;
|
|
NTSTATUS status;
|
|
uint64_t file_size = 0;
|
|
uint64_t pos = 0;
|
|
uint64_t allocation_size = 0;
|
|
uint64_t file_id = 0;
|
|
uint32_t access_mask = 0;
|
|
size_t len = 0;
|
|
|
|
if (INFO_LEVEL_IS_UNIX(info_level)) {
|
|
bool ok = false;
|
|
|
|
if (lp_smb1_unix_extensions() && req->posix_pathnames) {
|
|
DBG_DEBUG("SMB1 unix extensions activated\n");
|
|
ok = true;
|
|
}
|
|
|
|
if (lp_smb3_unix_extensions() &&
|
|
(fsp != NULL) &&
|
|
(fsp->posix_flags & FSP_POSIX_FLAGS_OPEN)) {
|
|
DBG_DEBUG("SMB2 posix open\n");
|
|
ok = true;
|
|
}
|
|
|
|
if (!ok) {
|
|
return NT_STATUS_INVALID_LEVEL;
|
|
}
|
|
}
|
|
|
|
DEBUG(5,("smbd_do_qfilepathinfo: %s (%s) level=%d max_data=%u\n",
|
|
smb_fname_str_dbg(smb_fname),
|
|
fsp_fnum_dbg(fsp),
|
|
info_level, max_data_bytes));
|
|
|
|
/*
|
|
* In case of querying a symlink in POSIX context,
|
|
* fsp will be NULL. fdos_mode() deals with it.
|
|
*/
|
|
if (fsp != NULL) {
|
|
smb_fname = fsp->fsp_name;
|
|
}
|
|
mode = fdos_mode(fsp);
|
|
psbuf = &smb_fname->st;
|
|
|
|
if (fsp != NULL) {
|
|
base_sp = fsp->base_fsp ?
|
|
&fsp->base_fsp->fsp_name->st :
|
|
&fsp->fsp_name->st;
|
|
} else {
|
|
base_sp = &smb_fname->st;
|
|
}
|
|
|
|
nlink = psbuf->st_ex_nlink;
|
|
|
|
if (nlink && (mode&FILE_ATTRIBUTE_DIRECTORY)) {
|
|
nlink = 1;
|
|
}
|
|
|
|
if ((nlink > 0) && delete_pending) {
|
|
nlink -= 1;
|
|
}
|
|
|
|
if (max_data_bytes + DIR_ENTRY_SAFETY_MARGIN < max_data_bytes) {
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
data_size = max_data_bytes + DIR_ENTRY_SAFETY_MARGIN;
|
|
*ppdata = (char *)SMB_REALLOC(*ppdata, data_size);
|
|
if (*ppdata == NULL) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
pdata = *ppdata;
|
|
dstart = pdata;
|
|
dend = dstart + data_size - 1;
|
|
|
|
if (!is_omit_timespec(&write_time_ts) &&
|
|
!INFO_LEVEL_IS_UNIX(info_level))
|
|
{
|
|
update_stat_ex_mtime(psbuf, write_time_ts);
|
|
}
|
|
|
|
create_time_ts = get_create_timespec(conn, fsp, smb_fname);
|
|
mtime_ts = psbuf->st_ex_mtime;
|
|
atime_ts = psbuf->st_ex_atime;
|
|
ctime_ts = get_change_timespec(conn, fsp, smb_fname);
|
|
|
|
if (lp_dos_filetime_resolution(SNUM(conn))) {
|
|
dos_filetime_timespec(&create_time_ts);
|
|
dos_filetime_timespec(&mtime_ts);
|
|
dos_filetime_timespec(&atime_ts);
|
|
dos_filetime_timespec(&ctime_ts);
|
|
}
|
|
|
|
create_time = convert_timespec_to_time_t(create_time_ts);
|
|
mtime = convert_timespec_to_time_t(mtime_ts);
|
|
atime = convert_timespec_to_time_t(atime_ts);
|
|
c_time = convert_timespec_to_time_t(ctime_ts);
|
|
|
|
p = strrchr_m(smb_fname->base_name,'/');
|
|
if (!p)
|
|
base_name = smb_fname->base_name;
|
|
else
|
|
base_name = p+1;
|
|
|
|
/* NT expects the name to be in an exact form of the *full*
|
|
filename. See the trans2 torture test */
|
|
if (ISDOT(base_name)) {
|
|
dos_fname = talloc_strdup(mem_ctx, "\\");
|
|
if (!dos_fname) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
} else {
|
|
dos_fname = talloc_asprintf(mem_ctx,
|
|
"\\%s",
|
|
smb_fname->base_name);
|
|
if (!dos_fname) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
if (is_named_stream(smb_fname)) {
|
|
dos_fname = talloc_asprintf(dos_fname, "%s",
|
|
smb_fname->stream_name);
|
|
if (!dos_fname) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
}
|
|
|
|
string_replace(dos_fname, '/', '\\');
|
|
}
|
|
|
|
allocation_size = SMB_VFS_GET_ALLOC_SIZE(conn, fsp, psbuf);
|
|
|
|
if (fsp == NULL || !fsp->fsp_flags.is_fsa) {
|
|
/* Do we have this path open ? */
|
|
files_struct *fsp1;
|
|
struct file_id fileid = vfs_file_id_from_sbuf(conn, psbuf);
|
|
fsp1 = file_find_di_first(conn->sconn, fileid, true);
|
|
if (fsp1 && fsp1->initial_allocation_size) {
|
|
allocation_size = SMB_VFS_GET_ALLOC_SIZE(conn, fsp1, psbuf);
|
|
}
|
|
}
|
|
|
|
if (!(mode & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
file_size = get_file_size_stat(psbuf);
|
|
}
|
|
|
|
if (fsp) {
|
|
pos = fh_get_position_information(fsp->fh);
|
|
}
|
|
|
|
if (fsp) {
|
|
access_mask = fsp->access_mask;
|
|
} else {
|
|
/* GENERIC_EXECUTE mapping from Windows */
|
|
access_mask = 0x12019F;
|
|
}
|
|
|
|
/* This should be an index number - looks like
|
|
dev/ino to me :-)
|
|
|
|
I think this causes us to fail the IFSKIT
|
|
BasicFileInformationTest. -tpot */
|
|
file_id = SMB_VFS_FS_FILE_ID(conn, base_sp);
|
|
|
|
*fixed_portion = 0;
|
|
|
|
switch (info_level) {
|
|
case SMB_INFO_STANDARD:
|
|
DEBUG(10,("smbd_do_qfilepathinfo: SMB_INFO_STANDARD\n"));
|
|
data_size = 22;
|
|
srv_put_dos_date2(pdata,l1_fdateCreation,create_time);
|
|
srv_put_dos_date2(pdata,l1_fdateLastAccess,atime);
|
|
srv_put_dos_date2(pdata,l1_fdateLastWrite,mtime); /* write time */
|
|
SIVAL(pdata,l1_cbFile,(uint32_t)file_size);
|
|
SIVAL(pdata,l1_cbFileAlloc,(uint32_t)allocation_size);
|
|
SSVAL(pdata,l1_attrFile,mode);
|
|
break;
|
|
|
|
case SMB_INFO_QUERY_EA_SIZE:
|
|
{
|
|
unsigned int ea_size =
|
|
estimate_ea_size(smb_fname->fsp);
|
|
DEBUG(10,("smbd_do_qfilepathinfo: SMB_INFO_QUERY_EA_SIZE\n"));
|
|
data_size = 26;
|
|
srv_put_dos_date2(pdata,0,create_time);
|
|
srv_put_dos_date2(pdata,4,atime);
|
|
srv_put_dos_date2(pdata,8,mtime); /* write time */
|
|
SIVAL(pdata,12,(uint32_t)file_size);
|
|
SIVAL(pdata,16,(uint32_t)allocation_size);
|
|
SSVAL(pdata,20,mode);
|
|
SIVAL(pdata,22,ea_size);
|
|
break;
|
|
}
|
|
|
|
case SMB_INFO_IS_NAME_VALID:
|
|
DEBUG(10,("smbd_do_qfilepathinfo: SMB_INFO_IS_NAME_VALID\n"));
|
|
if (fsp) {
|
|
/* os/2 needs this ? really ?*/
|
|
return NT_STATUS_DOS(ERRDOS, ERRbadfunc);
|
|
}
|
|
/* This is only reached for qpathinfo */
|
|
data_size = 0;
|
|
break;
|
|
|
|
case SMB_INFO_QUERY_EAS_FROM_LIST:
|
|
{
|
|
size_t total_ea_len = 0;
|
|
struct ea_list *ea_file_list = NULL;
|
|
DEBUG(10,("smbd_do_qfilepathinfo: SMB_INFO_QUERY_EAS_FROM_LIST\n"));
|
|
|
|
status =
|
|
get_ea_list_from_fsp(mem_ctx,
|
|
smb_fname->fsp,
|
|
&total_ea_len, &ea_file_list);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
|
|
ea_list = ea_list_union(ea_list, ea_file_list, &total_ea_len);
|
|
|
|
if (!ea_list || (total_ea_len > data_size)) {
|
|
data_size = 4;
|
|
SIVAL(pdata,0,4); /* EA List Length must be set to 4 if no EA's. */
|
|
break;
|
|
}
|
|
|
|
data_size = fill_ea_buffer(mem_ctx, pdata, data_size, conn, ea_list);
|
|
break;
|
|
}
|
|
|
|
case SMB_INFO_QUERY_ALL_EAS:
|
|
{
|
|
/* We have data_size bytes to put EA's into. */
|
|
size_t total_ea_len = 0;
|
|
DEBUG(10,("smbd_do_qfilepathinfo: SMB_INFO_QUERY_ALL_EAS\n"));
|
|
|
|
status = get_ea_list_from_fsp(mem_ctx,
|
|
smb_fname->fsp,
|
|
&total_ea_len, &ea_list);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
|
|
if (!ea_list || (total_ea_len > data_size)) {
|
|
data_size = 4;
|
|
SIVAL(pdata,0,4); /* EA List Length must be set to 4 if no EA's. */
|
|
break;
|
|
}
|
|
|
|
data_size = fill_ea_buffer(mem_ctx, pdata, data_size, conn, ea_list);
|
|
break;
|
|
}
|
|
|
|
case SMB2_FILE_FULL_EA_INFORMATION:
|
|
{
|
|
/* We have data_size bytes to put EA's into. */
|
|
size_t total_ea_len = 0;
|
|
struct ea_list *ea_file_list = NULL;
|
|
|
|
DEBUG(10,("smbd_do_qfilepathinfo: SMB2_INFO_QUERY_ALL_EAS\n"));
|
|
|
|
/*TODO: add filtering and index handling */
|
|
|
|
status =
|
|
get_ea_list_from_fsp(mem_ctx,
|
|
smb_fname->fsp,
|
|
&total_ea_len, &ea_file_list);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
if (!ea_file_list) {
|
|
return NT_STATUS_NO_EAS_ON_FILE;
|
|
}
|
|
|
|
status = fill_ea_chained_buffer(mem_ctx,
|
|
pdata,
|
|
data_size,
|
|
&data_size,
|
|
conn, ea_file_list);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SMB_FILE_BASIC_INFORMATION:
|
|
case SMB_QUERY_FILE_BASIC_INFO:
|
|
|
|
if (info_level == SMB_QUERY_FILE_BASIC_INFO) {
|
|
DEBUG(10,("smbd_do_qfilepathinfo: SMB_QUERY_FILE_BASIC_INFO\n"));
|
|
data_size = 36; /* w95 returns 40 bytes not 36 - why ?. */
|
|
} else {
|
|
DEBUG(10,("smbd_do_qfilepathinfo: SMB_FILE_BASIC_INFORMATION\n"));
|
|
data_size = 40;
|
|
SIVAL(pdata,36,0);
|
|
}
|
|
put_long_date_full_timespec(conn->ts_res,pdata,&create_time_ts);
|
|
put_long_date_full_timespec(conn->ts_res,pdata+8,&atime_ts);
|
|
put_long_date_full_timespec(conn->ts_res,pdata+16,&mtime_ts); /* write time */
|
|
put_long_date_full_timespec(conn->ts_res,pdata+24,&ctime_ts); /* change time */
|
|
SIVAL(pdata,32,mode);
|
|
|
|
DEBUG(5,("SMB_QFBI - "));
|
|
DEBUG(5,("create: %s ", ctime(&create_time)));
|
|
DEBUG(5,("access: %s ", ctime(&atime)));
|
|
DEBUG(5,("write: %s ", ctime(&mtime)));
|
|
DEBUG(5,("change: %s ", ctime(&c_time)));
|
|
DEBUG(5,("mode: %x\n", mode));
|
|
*fixed_portion = data_size;
|
|
break;
|
|
|
|
case SMB_FILE_STANDARD_INFORMATION:
|
|
case SMB_QUERY_FILE_STANDARD_INFO:
|
|
|
|
DEBUG(10,("smbd_do_qfilepathinfo: SMB_FILE_STANDARD_INFORMATION\n"));
|
|
data_size = 24;
|
|
SOFF_T(pdata,0,allocation_size);
|
|
SOFF_T(pdata,8,file_size);
|
|
SIVAL(pdata,16,nlink);
|
|
SCVAL(pdata,20,delete_pending?1:0);
|
|
SCVAL(pdata,21,(mode&FILE_ATTRIBUTE_DIRECTORY)?1:0);
|
|
SSVAL(pdata,22,0); /* Padding. */
|
|
*fixed_portion = 24;
|
|
break;
|
|
|
|
case SMB_FILE_EA_INFORMATION:
|
|
case SMB_QUERY_FILE_EA_INFO:
|
|
{
|
|
unsigned int ea_size =
|
|
estimate_ea_size(smb_fname->fsp);
|
|
DEBUG(10,("smbd_do_qfilepathinfo: SMB_FILE_EA_INFORMATION\n"));
|
|
data_size = 4;
|
|
*fixed_portion = 4;
|
|
SIVAL(pdata,0,ea_size);
|
|
break;
|
|
}
|
|
|
|
/* Get the 8.3 name - used if NT SMB was negotiated. */
|
|
case SMB_QUERY_FILE_ALT_NAME_INFO:
|
|
case SMB_FILE_ALTERNATE_NAME_INFORMATION:
|
|
{
|
|
char mangled_name[13];
|
|
DEBUG(10,("smbd_do_qfilepathinfo: SMB_FILE_ALTERNATE_NAME_INFORMATION\n"));
|
|
if (!name_to_8_3(base_name,mangled_name,
|
|
True,conn->params)) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
status = srvstr_push(dstart, flags2,
|
|
pdata+4, mangled_name,
|
|
PTR_DIFF(dend, pdata+4),
|
|
STR_UNICODE, &len);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
data_size = 4 + len;
|
|
SIVAL(pdata,0,len);
|
|
*fixed_portion = 8;
|
|
break;
|
|
}
|
|
|
|
case SMB_QUERY_FILE_NAME_INFO:
|
|
{
|
|
/*
|
|
this must be *exactly* right for ACLs on mapped drives to work
|
|
*/
|
|
status = srvstr_push(dstart, flags2,
|
|
pdata+4, dos_fname,
|
|
PTR_DIFF(dend, pdata+4),
|
|
STR_UNICODE, &len);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
DEBUG(10,("smbd_do_qfilepathinfo: SMB_QUERY_FILE_NAME_INFO\n"));
|
|
data_size = 4 + len;
|
|
SIVAL(pdata,0,len);
|
|
break;
|
|
}
|
|
|
|
case SMB_FILE_NORMALIZED_NAME_INFORMATION:
|
|
{
|
|
char *nfname = NULL;
|
|
|
|
if (fsp == NULL || !fsp->conn->sconn->using_smb2) {
|
|
return NT_STATUS_INVALID_LEVEL;
|
|
}
|
|
|
|
nfname = talloc_strdup(mem_ctx, smb_fname->base_name);
|
|
if (nfname == NULL) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
if (ISDOT(nfname)) {
|
|
nfname[0] = '\0';
|
|
}
|
|
string_replace(nfname, '/', '\\');
|
|
|
|
if (fsp_is_alternate_stream(fsp)) {
|
|
const char *s = smb_fname->stream_name;
|
|
const char *e = NULL;
|
|
size_t n;
|
|
|
|
SMB_ASSERT(s[0] != '\0');
|
|
|
|
/*
|
|
* smb_fname->stream_name is in form
|
|
* of ':StrEam:$DATA', but we should only
|
|
* append ':StrEam' here.
|
|
*/
|
|
|
|
e = strchr(&s[1], ':');
|
|
if (e == NULL) {
|
|
n = strlen(s);
|
|
} else {
|
|
n = PTR_DIFF(e, s);
|
|
}
|
|
nfname = talloc_strndup_append(nfname, s, n);
|
|
if (nfname == NULL) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
}
|
|
|
|
status = srvstr_push(dstart, flags2,
|
|
pdata+4, nfname,
|
|
PTR_DIFF(dend, pdata+4),
|
|
STR_UNICODE, &len);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
DEBUG(10,("smbd_do_qfilepathinfo: SMB_FILE_NORMALIZED_NAME_INFORMATION\n"));
|
|
data_size = 4 + len;
|
|
SIVAL(pdata,0,len);
|
|
*fixed_portion = 8;
|
|
break;
|
|
}
|
|
|
|
case SMB_FILE_ALLOCATION_INFORMATION:
|
|
case SMB_QUERY_FILE_ALLOCATION_INFO:
|
|
DEBUG(10,("smbd_do_qfilepathinfo: SMB_FILE_ALLOCATION_INFORMATION\n"));
|
|
data_size = 8;
|
|
SOFF_T(pdata,0,allocation_size);
|
|
break;
|
|
|
|
case SMB_FILE_END_OF_FILE_INFORMATION:
|
|
case SMB_QUERY_FILE_END_OF_FILEINFO:
|
|
DEBUG(10,("smbd_do_qfilepathinfo: SMB_FILE_END_OF_FILE_INFORMATION\n"));
|
|
data_size = 8;
|
|
SOFF_T(pdata,0,file_size);
|
|
break;
|
|
|
|
case SMB_QUERY_FILE_ALL_INFO:
|
|
case SMB_FILE_ALL_INFORMATION:
|
|
{
|
|
unsigned int ea_size =
|
|
estimate_ea_size(smb_fname->fsp);
|
|
DEBUG(10,("smbd_do_qfilepathinfo: SMB_FILE_ALL_INFORMATION\n"));
|
|
put_long_date_full_timespec(conn->ts_res,pdata,&create_time_ts);
|
|
put_long_date_full_timespec(conn->ts_res,pdata+8,&atime_ts);
|
|
put_long_date_full_timespec(conn->ts_res,pdata+16,&mtime_ts); /* write time */
|
|
put_long_date_full_timespec(conn->ts_res,pdata+24,&ctime_ts); /* change time */
|
|
SIVAL(pdata,32,mode);
|
|
SIVAL(pdata,36,0); /* padding. */
|
|
pdata += 40;
|
|
SOFF_T(pdata,0,allocation_size);
|
|
SOFF_T(pdata,8,file_size);
|
|
SIVAL(pdata,16,nlink);
|
|
SCVAL(pdata,20,delete_pending);
|
|
SCVAL(pdata,21,(mode&FILE_ATTRIBUTE_DIRECTORY)?1:0);
|
|
SSVAL(pdata,22,0);
|
|
pdata += 24;
|
|
SIVAL(pdata,0,ea_size);
|
|
pdata += 4; /* EA info */
|
|
status = srvstr_push(dstart, flags2,
|
|
pdata+4, dos_fname,
|
|
PTR_DIFF(dend, pdata+4),
|
|
STR_UNICODE, &len);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
SIVAL(pdata,0,len);
|
|
pdata += 4 + len;
|
|
data_size = PTR_DIFF(pdata,(*ppdata));
|
|
*fixed_portion = 10;
|
|
break;
|
|
}
|
|
|
|
case SMB2_FILE_ALL_INFORMATION:
|
|
{
|
|
unsigned int ea_size =
|
|
estimate_ea_size(smb_fname->fsp);
|
|
DEBUG(10,("smbd_do_qfilepathinfo: SMB2_FILE_ALL_INFORMATION\n"));
|
|
put_long_date_full_timespec(conn->ts_res,pdata+0x00,&create_time_ts);
|
|
put_long_date_full_timespec(conn->ts_res,pdata+0x08,&atime_ts);
|
|
put_long_date_full_timespec(conn->ts_res,pdata+0x10,&mtime_ts); /* write time */
|
|
put_long_date_full_timespec(conn->ts_res,pdata+0x18,&ctime_ts); /* change time */
|
|
SIVAL(pdata, 0x20, mode);
|
|
SIVAL(pdata, 0x24, 0); /* padding. */
|
|
SBVAL(pdata, 0x28, allocation_size);
|
|
SBVAL(pdata, 0x30, file_size);
|
|
SIVAL(pdata, 0x38, nlink);
|
|
SCVAL(pdata, 0x3C, delete_pending);
|
|
SCVAL(pdata, 0x3D, (mode&FILE_ATTRIBUTE_DIRECTORY)?1:0);
|
|
SSVAL(pdata, 0x3E, 0); /* padding */
|
|
SBVAL(pdata, 0x40, file_id);
|
|
SIVAL(pdata, 0x48, ea_size);
|
|
SIVAL(pdata, 0x4C, access_mask);
|
|
SBVAL(pdata, 0x50, pos);
|
|
SIVAL(pdata, 0x58, mode); /*TODO: mode != mode fix this!!! */
|
|
SIVAL(pdata, 0x5C, 0); /* No alignment needed. */
|
|
|
|
pdata += 0x60;
|
|
|
|
status = srvstr_push(dstart, flags2,
|
|
pdata+4, dos_fname,
|
|
PTR_DIFF(dend, pdata+4),
|
|
STR_UNICODE, &len);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
SIVAL(pdata,0,len);
|
|
pdata += 4 + len;
|
|
data_size = PTR_DIFF(pdata,(*ppdata));
|
|
*fixed_portion = 104;
|
|
break;
|
|
}
|
|
case SMB_FILE_INTERNAL_INFORMATION:
|
|
|
|
DEBUG(10,("smbd_do_qfilepathinfo: SMB_FILE_INTERNAL_INFORMATION\n"));
|
|
SBVAL(pdata, 0, file_id);
|
|
data_size = 8;
|
|
*fixed_portion = 8;
|
|
break;
|
|
|
|
case SMB_FILE_ACCESS_INFORMATION:
|
|
DEBUG(10,("smbd_do_qfilepathinfo: SMB_FILE_ACCESS_INFORMATION\n"));
|
|
SIVAL(pdata, 0, access_mask);
|
|
data_size = 4;
|
|
*fixed_portion = 4;
|
|
break;
|
|
|
|
case SMB_FILE_NAME_INFORMATION:
|
|
/* Pathname with leading '\'. */
|
|
{
|
|
size_t byte_len;
|
|
byte_len = dos_PutUniCode(pdata+4,dos_fname,(size_t)max_data_bytes,False);
|
|
DEBUG(10,("smbd_do_qfilepathinfo: SMB_FILE_NAME_INFORMATION\n"));
|
|
SIVAL(pdata,0,byte_len);
|
|
data_size = 4 + byte_len;
|
|
break;
|
|
}
|
|
|
|
case SMB_FILE_DISPOSITION_INFORMATION:
|
|
DEBUG(10,("smbd_do_qfilepathinfo: SMB_FILE_DISPOSITION_INFORMATION\n"));
|
|
data_size = 1;
|
|
SCVAL(pdata,0,delete_pending);
|
|
*fixed_portion = 1;
|
|
break;
|
|
|
|
case SMB_FILE_POSITION_INFORMATION:
|
|
DEBUG(10,("smbd_do_qfilepathinfo: SMB_FILE_POSITION_INFORMATION\n"));
|
|
data_size = 8;
|
|
SOFF_T(pdata,0,pos);
|
|
*fixed_portion = 8;
|
|
break;
|
|
|
|
case SMB_FILE_MODE_INFORMATION:
|
|
DEBUG(10,("smbd_do_qfilepathinfo: SMB_FILE_MODE_INFORMATION\n"));
|
|
SIVAL(pdata,0,mode);
|
|
data_size = 4;
|
|
*fixed_portion = 4;
|
|
break;
|
|
|
|
case SMB_FILE_ALIGNMENT_INFORMATION:
|
|
DEBUG(10,("smbd_do_qfilepathinfo: SMB_FILE_ALIGNMENT_INFORMATION\n"));
|
|
SIVAL(pdata,0,0); /* No alignment needed. */
|
|
data_size = 4;
|
|
*fixed_portion = 4;
|
|
break;
|
|
|
|
/*
|
|
* NT4 server just returns "invalid query" to this - if we try
|
|
* to answer it then NTws gets a BSOD! (tridge). W2K seems to
|
|
* want this. JRA.
|
|
*/
|
|
/* The first statement above is false - verified using Thursby
|
|
* client against NT4 -- gcolley.
|
|
*/
|
|
case SMB_QUERY_FILE_STREAM_INFO:
|
|
case SMB_FILE_STREAM_INFORMATION: {
|
|
unsigned int num_streams = 0;
|
|
struct stream_struct *streams = NULL;
|
|
|
|
DEBUG(10,("smbd_do_qfilepathinfo: "
|
|
"SMB_FILE_STREAM_INFORMATION\n"));
|
|
|
|
if (is_ntfs_stream_smb_fname(smb_fname)) {
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
status = vfs_fstreaminfo(fsp,
|
|
mem_ctx,
|
|
&num_streams,
|
|
&streams);
|
|
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
DEBUG(10, ("could not get stream info: %s\n",
|
|
nt_errstr(status)));
|
|
return status;
|
|
}
|
|
|
|
status = marshall_stream_info(num_streams, streams,
|
|
pdata, max_data_bytes,
|
|
&data_size);
|
|
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
DEBUG(10, ("marshall_stream_info failed: %s\n",
|
|
nt_errstr(status)));
|
|
TALLOC_FREE(streams);
|
|
return status;
|
|
}
|
|
|
|
TALLOC_FREE(streams);
|
|
|
|
*fixed_portion = 32;
|
|
|
|
break;
|
|
}
|
|
case SMB_QUERY_COMPRESSION_INFO:
|
|
case SMB_FILE_COMPRESSION_INFORMATION:
|
|
DEBUG(10,("smbd_do_qfilepathinfo: SMB_FILE_COMPRESSION_INFORMATION\n"));
|
|
SOFF_T(pdata,0,file_size);
|
|
SIVAL(pdata,8,0); /* ??? */
|
|
SIVAL(pdata,12,0); /* ??? */
|
|
data_size = 16;
|
|
*fixed_portion = 16;
|
|
break;
|
|
|
|
case SMB_FILE_NETWORK_OPEN_INFORMATION:
|
|
DEBUG(10,("smbd_do_qfilepathinfo: SMB_FILE_NETWORK_OPEN_INFORMATION\n"));
|
|
put_long_date_full_timespec(conn->ts_res,pdata,&create_time_ts);
|
|
put_long_date_full_timespec(conn->ts_res,pdata+8,&atime_ts);
|
|
put_long_date_full_timespec(conn->ts_res,pdata+16,&mtime_ts); /* write time */
|
|
put_long_date_full_timespec(conn->ts_res,pdata+24,&ctime_ts); /* change time */
|
|
SOFF_T(pdata,32,allocation_size);
|
|
SOFF_T(pdata,40,file_size);
|
|
SIVAL(pdata,48,mode);
|
|
SIVAL(pdata,52,0); /* ??? */
|
|
data_size = 56;
|
|
*fixed_portion = 56;
|
|
break;
|
|
|
|
case SMB_FILE_ATTRIBUTE_TAG_INFORMATION:
|
|
DEBUG(10,("smbd_do_qfilepathinfo: SMB_FILE_ATTRIBUTE_TAG_INFORMATION\n"));
|
|
SIVAL(pdata,0,mode);
|
|
SIVAL(pdata,4,0);
|
|
data_size = 8;
|
|
*fixed_portion = 8;
|
|
break;
|
|
|
|
/*
|
|
* CIFS UNIX Extensions.
|
|
*/
|
|
|
|
case SMB_QUERY_FILE_UNIX_BASIC:
|
|
|
|
pdata = store_file_unix_basic(conn, pdata, fsp, psbuf);
|
|
data_size = PTR_DIFF(pdata,(*ppdata));
|
|
|
|
DEBUG(4,("smbd_do_qfilepathinfo: "
|
|
"SMB_QUERY_FILE_UNIX_BASIC\n"));
|
|
dump_data(4, (uint8_t *)(*ppdata), data_size);
|
|
|
|
break;
|
|
|
|
case SMB_QUERY_FILE_UNIX_INFO2:
|
|
|
|
pdata = store_file_unix_basic_info2(conn, pdata, fsp, psbuf);
|
|
data_size = PTR_DIFF(pdata,(*ppdata));
|
|
|
|
{
|
|
int i;
|
|
DEBUG(4,("smbd_do_qfilepathinfo: SMB_QUERY_FILE_UNIX_INFO2 "));
|
|
|
|
for (i=0; i<100; i++)
|
|
DEBUG(4,("%d=%x, ",i, (*ppdata)[i]));
|
|
DEBUG(4,("\n"));
|
|
}
|
|
|
|
break;
|
|
|
|
case SMB_QUERY_FILE_UNIX_LINK:
|
|
{
|
|
status = smb_unix_read_symlink(conn,
|
|
req,
|
|
smb_fname,
|
|
pdata,
|
|
data_size,
|
|
&data_size);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
break;
|
|
}
|
|
|
|
#if defined(HAVE_POSIX_ACLS)
|
|
case SMB_QUERY_POSIX_ACL:
|
|
{
|
|
status = smb_query_posix_acl(conn,
|
|
req,
|
|
fsp,
|
|
smb_fname,
|
|
pdata,
|
|
data_size,
|
|
&data_size);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* SMB2 UNIX Extensions.
|
|
*/
|
|
case SMB2_FILE_POSIX_INFORMATION_INTERNAL:
|
|
{
|
|
uint8_t *buf = NULL;
|
|
ssize_t plen = 0;
|
|
|
|
if (!(conn->sconn->using_smb2)) {
|
|
return NT_STATUS_INVALID_LEVEL;
|
|
}
|
|
if (!lp_smb3_unix_extensions()) {
|
|
return NT_STATUS_INVALID_LEVEL;
|
|
}
|
|
if (fsp == NULL) {
|
|
return NT_STATUS_INVALID_HANDLE;
|
|
}
|
|
if (!(fsp->posix_flags & FSP_POSIX_FLAGS_OPEN)) {
|
|
return NT_STATUS_INVALID_LEVEL;
|
|
}
|
|
|
|
/* Determine the size of the posix info context */
|
|
plen = store_smb2_posix_info(conn,
|
|
&smb_fname->st,
|
|
0,
|
|
mode,
|
|
NULL,
|
|
0);
|
|
if (plen == -1 || data_size < plen) {
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
buf = talloc_zero_size(mem_ctx, plen);
|
|
if (buf == NULL) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
/* Store the context in buf */
|
|
store_smb2_posix_info(conn,
|
|
&smb_fname->st,
|
|
0,
|
|
mode,
|
|
buf,
|
|
plen);
|
|
memcpy(pdata, buf, plen);
|
|
data_size = plen;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
return NT_STATUS_INVALID_LEVEL;
|
|
}
|
|
|
|
*pdata_size = data_size;
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Set a hard link (called by UNIX extensions and by NT rename with HARD link
|
|
code.
|
|
****************************************************************************/
|
|
|
|
NTSTATUS hardlink_internals(TALLOC_CTX *ctx,
|
|
connection_struct *conn,
|
|
struct smb_request *req,
|
|
bool overwrite_if_exists,
|
|
struct files_struct *old_dirfsp,
|
|
const struct smb_filename *smb_fname_old,
|
|
struct files_struct *new_dirfsp,
|
|
struct smb_filename *smb_fname_new)
|
|
{
|
|
NTSTATUS status = NT_STATUS_OK;
|
|
int ret;
|
|
bool ok;
|
|
struct smb_filename *parent_fname_old = NULL;
|
|
struct smb_filename *base_name_old = NULL;
|
|
struct smb_filename *parent_fname_new = NULL;
|
|
struct smb_filename *base_name_new = NULL;
|
|
|
|
/* source must already exist. */
|
|
if (!VALID_STAT(smb_fname_old->st)) {
|
|
status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
goto out;
|
|
}
|
|
|
|
/* No links from a directory. */
|
|
if (S_ISDIR(smb_fname_old->st.st_ex_mode)) {
|
|
status = NT_STATUS_FILE_IS_A_DIRECTORY;
|
|
goto out;
|
|
}
|
|
|
|
/* Setting a hardlink to/from a stream isn't currently supported. */
|
|
ok = is_ntfs_stream_smb_fname(smb_fname_old);
|
|
if (ok) {
|
|
DBG_DEBUG("Old name has streams\n");
|
|
status = NT_STATUS_INVALID_PARAMETER;
|
|
goto out;
|
|
}
|
|
ok = is_ntfs_stream_smb_fname(smb_fname_new);
|
|
if (ok) {
|
|
DBG_DEBUG("New name has streams\n");
|
|
status = NT_STATUS_INVALID_PARAMETER;
|
|
goto out;
|
|
}
|
|
|
|
status = parent_pathref(talloc_tos(),
|
|
conn->cwd_fsp,
|
|
smb_fname_old,
|
|
&parent_fname_old,
|
|
&base_name_old);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
goto out;
|
|
}
|
|
|
|
status = parent_pathref(talloc_tos(),
|
|
conn->cwd_fsp,
|
|
smb_fname_new,
|
|
&parent_fname_new,
|
|
&base_name_new);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
goto out;
|
|
}
|
|
|
|
if (VALID_STAT(smb_fname_new->st)) {
|
|
if (overwrite_if_exists) {
|
|
if (S_ISDIR(smb_fname_new->st.st_ex_mode)) {
|
|
status = NT_STATUS_FILE_IS_A_DIRECTORY;
|
|
goto out;
|
|
}
|
|
status = unlink_internals(conn,
|
|
req,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL, /* new_dirfsp */
|
|
smb_fname_new);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
goto out;
|
|
}
|
|
} else {
|
|
/* Disallow if newname already exists. */
|
|
status = NT_STATUS_OBJECT_NAME_COLLISION;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
DEBUG(10,("hardlink_internals: doing hard link %s -> %s\n",
|
|
smb_fname_old->base_name, smb_fname_new->base_name));
|
|
|
|
ret = SMB_VFS_LINKAT(conn,
|
|
parent_fname_old->fsp,
|
|
base_name_old,
|
|
parent_fname_new->fsp,
|
|
base_name_new,
|
|
0);
|
|
|
|
if (ret != 0) {
|
|
status = map_nt_error_from_unix(errno);
|
|
DEBUG(3,("hardlink_internals: Error %s hard link %s -> %s\n",
|
|
nt_errstr(status), smb_fname_old->base_name,
|
|
smb_fname_new->base_name));
|
|
}
|
|
|
|
out:
|
|
|
|
TALLOC_FREE(parent_fname_old);
|
|
TALLOC_FREE(parent_fname_new);
|
|
return status;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Deal with setting the time from any of the setfilepathinfo functions.
|
|
NOTE !!!! The check for FILE_WRITE_ATTRIBUTES access must be done *before*
|
|
calling this function.
|
|
****************************************************************************/
|
|
|
|
NTSTATUS smb_set_file_time(connection_struct *conn,
|
|
files_struct *fsp,
|
|
struct smb_filename *smb_fname,
|
|
struct smb_file_time *ft,
|
|
bool setting_write_time)
|
|
{
|
|
struct files_struct *set_fsp = NULL;
|
|
struct timeval_buf tbuf[4];
|
|
uint32_t action =
|
|
FILE_NOTIFY_CHANGE_LAST_ACCESS
|
|
|FILE_NOTIFY_CHANGE_LAST_WRITE
|
|
|FILE_NOTIFY_CHANGE_CREATION;
|
|
int ret;
|
|
|
|
if (!VALID_STAT(smb_fname->st)) {
|
|
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
|
|
if (fsp == NULL) {
|
|
/* A symlink */
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
set_fsp = metadata_fsp(fsp);
|
|
|
|
/* get some defaults (no modifications) if any info is zero or -1. */
|
|
if (is_omit_timespec(&ft->create_time)) {
|
|
action &= ~FILE_NOTIFY_CHANGE_CREATION;
|
|
}
|
|
|
|
if (is_omit_timespec(&ft->atime)) {
|
|
action &= ~FILE_NOTIFY_CHANGE_LAST_ACCESS;
|
|
}
|
|
|
|
if (is_omit_timespec(&ft->mtime)) {
|
|
action &= ~FILE_NOTIFY_CHANGE_LAST_WRITE;
|
|
}
|
|
|
|
if (!setting_write_time) {
|
|
/* ft->mtime comes from change time, not write time. */
|
|
action &= ~FILE_NOTIFY_CHANGE_LAST_WRITE;
|
|
}
|
|
|
|
/* Ensure the resolution is the correct for
|
|
* what we can store on this filesystem. */
|
|
|
|
round_timespec(conn->ts_res, &ft->create_time);
|
|
round_timespec(conn->ts_res, &ft->ctime);
|
|
round_timespec(conn->ts_res, &ft->atime);
|
|
round_timespec(conn->ts_res, &ft->mtime);
|
|
|
|
DBG_DEBUG("smb_set_filetime: actime: %s\n ",
|
|
timespec_string_buf(&ft->atime, true, &tbuf[0]));
|
|
DBG_DEBUG("smb_set_filetime: modtime: %s\n ",
|
|
timespec_string_buf(&ft->mtime, true, &tbuf[1]));
|
|
DBG_DEBUG("smb_set_filetime: ctime: %s\n ",
|
|
timespec_string_buf(&ft->ctime, true, &tbuf[2]));
|
|
DBG_DEBUG("smb_set_file_time: createtime: %s\n ",
|
|
timespec_string_buf(&ft->create_time, true, &tbuf[3]));
|
|
|
|
if (setting_write_time) {
|
|
/*
|
|
* This was a Windows setfileinfo on an open file.
|
|
* NT does this a lot. We also need to
|
|
* set the time here, as it can be read by
|
|
* FindFirst/FindNext and with the patch for bug #2045
|
|
* in smbd/fileio.c it ensures that this timestamp is
|
|
* kept sticky even after a write. We save the request
|
|
* away and will set it on file close and after a write. JRA.
|
|
*/
|
|
|
|
DBG_DEBUG("setting pending modtime to %s\n",
|
|
timespec_string_buf(&ft->mtime, true, &tbuf[0]));
|
|
|
|
if (set_fsp != NULL) {
|
|
set_sticky_write_time_fsp(set_fsp, ft->mtime);
|
|
} else {
|
|
set_sticky_write_time_path(
|
|
vfs_file_id_from_sbuf(conn, &smb_fname->st),
|
|
ft->mtime);
|
|
}
|
|
}
|
|
|
|
DEBUG(10,("smb_set_file_time: setting utimes to modified values.\n"));
|
|
|
|
ret = file_ntimes(conn, set_fsp, ft);
|
|
if (ret != 0) {
|
|
return map_nt_error_from_unix(errno);
|
|
}
|
|
|
|
notify_fname(conn, NOTIFY_ACTION_MODIFIED, action,
|
|
smb_fname->base_name);
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Deal with setting the dosmode from any of the setfilepathinfo functions.
|
|
NB. The check for FILE_WRITE_ATTRIBUTES access on this path must have been
|
|
done before calling this function.
|
|
****************************************************************************/
|
|
|
|
static NTSTATUS smb_set_file_dosmode(connection_struct *conn,
|
|
struct files_struct *fsp,
|
|
uint32_t dosmode)
|
|
{
|
|
struct files_struct *dos_fsp = NULL;
|
|
uint32_t current_dosmode;
|
|
int ret;
|
|
|
|
if (!VALID_STAT(fsp->fsp_name->st)) {
|
|
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
|
|
dos_fsp = metadata_fsp(fsp);
|
|
|
|
if (dosmode != 0) {
|
|
if (S_ISDIR(fsp->fsp_name->st.st_ex_mode)) {
|
|
dosmode |= FILE_ATTRIBUTE_DIRECTORY;
|
|
} else {
|
|
dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;
|
|
}
|
|
}
|
|
|
|
DBG_DEBUG("dosmode: 0x%" PRIx32 "\n", dosmode);
|
|
|
|
/* check the mode isn't different, before changing it */
|
|
if (dosmode == 0) {
|
|
return NT_STATUS_OK;
|
|
}
|
|
current_dosmode = fdos_mode(dos_fsp);
|
|
if (dosmode == current_dosmode) {
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
DBG_DEBUG("file %s : setting dos mode 0x%" PRIx32 "\n",
|
|
fsp_str_dbg(dos_fsp), dosmode);
|
|
|
|
ret = file_set_dosmode(conn, dos_fsp->fsp_name, dosmode, NULL, false);
|
|
if (ret != 0) {
|
|
DBG_WARNING("file_set_dosmode of %s failed: %s\n",
|
|
fsp_str_dbg(dos_fsp), strerror(errno));
|
|
return map_nt_error_from_unix(errno);
|
|
}
|
|
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Deal with setting the size from any of the setfilepathinfo functions.
|
|
****************************************************************************/
|
|
|
|
NTSTATUS smb_set_file_size(connection_struct *conn,
|
|
struct smb_request *req,
|
|
files_struct *fsp,
|
|
struct smb_filename *smb_fname,
|
|
const SMB_STRUCT_STAT *psbuf,
|
|
off_t size,
|
|
bool fail_after_createfile)
|
|
{
|
|
NTSTATUS status = NT_STATUS_OK;
|
|
files_struct *new_fsp = NULL;
|
|
|
|
if (!VALID_STAT(*psbuf)) {
|
|
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
|
|
DBG_INFO("size: %"PRIu64", file_size_stat=%"PRIu64"\n",
|
|
(uint64_t)size,
|
|
get_file_size_stat(psbuf));
|
|
|
|
if (size == get_file_size_stat(psbuf)) {
|
|
if (fsp == NULL) {
|
|
return NT_STATUS_OK;
|
|
}
|
|
if (!fsp->fsp_flags.modified) {
|
|
return NT_STATUS_OK;
|
|
}
|
|
trigger_write_time_update_immediate(fsp);
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
DEBUG(10,("smb_set_file_size: file %s : setting new size to %.0f\n",
|
|
smb_fname_str_dbg(smb_fname), (double)size));
|
|
|
|
if (fsp &&
|
|
!fsp->fsp_flags.is_pathref &&
|
|
fsp_get_io_fd(fsp) != -1)
|
|
{
|
|
/* Handle based call. */
|
|
if (!(fsp->access_mask & FILE_WRITE_DATA)) {
|
|
return NT_STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
if (vfs_set_filelen(fsp, size) == -1) {
|
|
return map_nt_error_from_unix(errno);
|
|
}
|
|
trigger_write_time_update_immediate(fsp);
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
status = SMB_VFS_CREATE_FILE(
|
|
conn, /* conn */
|
|
req, /* req */
|
|
NULL, /* dirfsp */
|
|
smb_fname, /* fname */
|
|
FILE_WRITE_DATA, /* access_mask */
|
|
(FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
|
|
FILE_SHARE_DELETE),
|
|
FILE_OPEN, /* create_disposition*/
|
|
0, /* create_options */
|
|
FILE_ATTRIBUTE_NORMAL, /* file_attributes */
|
|
0, /* oplock_request */
|
|
NULL, /* lease */
|
|
0, /* allocation_size */
|
|
0, /* private_flags */
|
|
NULL, /* sd */
|
|
NULL, /* ea_list */
|
|
&new_fsp, /* result */
|
|
NULL, /* pinfo */
|
|
NULL, NULL); /* create context */
|
|
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
/* NB. We check for open_was_deferred in the caller. */
|
|
return status;
|
|
}
|
|
|
|
/* See RAW-SFILEINFO-END-OF-FILE */
|
|
if (fail_after_createfile) {
|
|
close_file_free(req, &new_fsp, NORMAL_CLOSE);
|
|
return NT_STATUS_INVALID_LEVEL;
|
|
}
|
|
|
|
if (vfs_set_filelen(new_fsp, size) == -1) {
|
|
status = map_nt_error_from_unix(errno);
|
|
close_file_free(req, &new_fsp, NORMAL_CLOSE);
|
|
return status;
|
|
}
|
|
|
|
trigger_write_time_update_immediate(new_fsp);
|
|
close_file_free(req, &new_fsp, NORMAL_CLOSE);
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Deal with SMB_INFO_SET_EA.
|
|
****************************************************************************/
|
|
|
|
static NTSTATUS smb_info_set_ea(connection_struct *conn,
|
|
const char *pdata,
|
|
int total_data,
|
|
files_struct *fsp,
|
|
struct smb_filename *smb_fname)
|
|
{
|
|
struct ea_list *ea_list = NULL;
|
|
TALLOC_CTX *ctx = NULL;
|
|
NTSTATUS status = NT_STATUS_OK;
|
|
|
|
if (total_data < 10) {
|
|
|
|
/* OS/2 workplace shell seems to send SET_EA requests of "null"
|
|
length. They seem to have no effect. Bug #3212. JRA */
|
|
|
|
if ((total_data == 4) && (IVAL(pdata,0) == 4)) {
|
|
/* We're done. We only get EA info in this call. */
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (IVAL(pdata,0) > total_data) {
|
|
DEBUG(10,("smb_info_set_ea: bad total data size (%u) > %u\n",
|
|
IVAL(pdata,0), (unsigned int)total_data));
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
ctx = talloc_tos();
|
|
ea_list = read_ea_list(ctx, pdata + 4, total_data - 4);
|
|
if (!ea_list) {
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (fsp == NULL) {
|
|
/*
|
|
* The only way fsp can be NULL here is if
|
|
* smb_fname points at a symlink and
|
|
* and we're in POSIX context.
|
|
* Ensure this is the case.
|
|
*
|
|
* In this case we cannot set the EA.
|
|
*/
|
|
SMB_ASSERT(smb_fname->flags & SMB_FILENAME_POSIX_PATH);
|
|
return NT_STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
status = set_ea(conn, fsp, ea_list);
|
|
|
|
return status;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Deal with SMB_FILE_FULL_EA_INFORMATION set.
|
|
****************************************************************************/
|
|
|
|
static NTSTATUS smb_set_file_full_ea_info(connection_struct *conn,
|
|
const char *pdata,
|
|
int total_data,
|
|
files_struct *fsp)
|
|
{
|
|
struct ea_list *ea_list = NULL;
|
|
NTSTATUS status;
|
|
|
|
if (fsp == NULL) {
|
|
return NT_STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
if (!lp_ea_support(SNUM(conn))) {
|
|
DEBUG(10, ("smb_set_file_full_ea_info - ea_len = %u but "
|
|
"EA's not supported.\n",
|
|
(unsigned int)total_data));
|
|
return NT_STATUS_EAS_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (total_data < 10) {
|
|
DEBUG(10, ("smb_set_file_full_ea_info - ea_len = %u "
|
|
"too small.\n",
|
|
(unsigned int)total_data));
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
ea_list = read_nttrans_ea_list(talloc_tos(),
|
|
pdata,
|
|
total_data);
|
|
|
|
if (!ea_list) {
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
status = set_ea(conn, fsp, ea_list);
|
|
|
|
DEBUG(10, ("smb_set_file_full_ea_info on file %s returned %s\n",
|
|
smb_fname_str_dbg(fsp->fsp_name),
|
|
nt_errstr(status) ));
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
Deal with SMB_SET_FILE_DISPOSITION_INFO.
|
|
****************************************************************************/
|
|
|
|
NTSTATUS smb_set_file_disposition_info(connection_struct *conn,
|
|
const char *pdata,
|
|
int total_data,
|
|
files_struct *fsp,
|
|
struct smb_filename *smb_fname)
|
|
{
|
|
NTSTATUS status = NT_STATUS_OK;
|
|
bool delete_on_close;
|
|
uint32_t dosmode = 0;
|
|
|
|
if (total_data < 1) {
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (fsp == NULL) {
|
|
return NT_STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
delete_on_close = (CVAL(pdata,0) ? True : False);
|
|
dosmode = fdos_mode(fsp);
|
|
|
|
DEBUG(10,("smb_set_file_disposition_info: file %s, dosmode = %u, "
|
|
"delete_on_close = %u\n",
|
|
smb_fname_str_dbg(smb_fname),
|
|
(unsigned int)dosmode,
|
|
(unsigned int)delete_on_close ));
|
|
|
|
if (delete_on_close) {
|
|
status = can_set_delete_on_close(fsp, dosmode);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
}
|
|
|
|
/* The set is across all open files on this dev/inode pair. */
|
|
if (!set_delete_on_close(fsp, delete_on_close,
|
|
conn->session_info->security_token,
|
|
conn->session_info->unix_token)) {
|
|
return NT_STATUS_ACCESS_DENIED;
|
|
}
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Deal with SMB_FILE_POSITION_INFORMATION.
|
|
****************************************************************************/
|
|
|
|
static NTSTATUS smb_file_position_information(connection_struct *conn,
|
|
const char *pdata,
|
|
int total_data,
|
|
files_struct *fsp)
|
|
{
|
|
uint64_t position_information;
|
|
|
|
if (total_data < 8) {
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (fsp == NULL) {
|
|
/* Ignore on pathname based set. */
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
position_information = (uint64_t)IVAL(pdata,0);
|
|
position_information |= (((uint64_t)IVAL(pdata,4)) << 32);
|
|
|
|
DEBUG(10,("smb_file_position_information: Set file position "
|
|
"information for file %s to %.0f\n", fsp_str_dbg(fsp),
|
|
(double)position_information));
|
|
fh_set_position_information(fsp->fh, position_information);
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Deal with SMB_FILE_MODE_INFORMATION.
|
|
****************************************************************************/
|
|
|
|
static NTSTATUS smb_file_mode_information(connection_struct *conn,
|
|
const char *pdata,
|
|
int total_data)
|
|
{
|
|
uint32_t mode;
|
|
|
|
if (total_data < 4) {
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
mode = IVAL(pdata,0);
|
|
if (mode != 0 && mode != 2 && mode != 4 && mode != 6) {
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Deal with SMB2_FILE_RENAME_INFORMATION_INTERNAL
|
|
****************************************************************************/
|
|
|
|
static NTSTATUS smb2_file_rename_information(connection_struct *conn,
|
|
struct smb_request *req,
|
|
const char *pdata,
|
|
int total_data,
|
|
files_struct *fsp,
|
|
struct smb_filename *smb_fname_src)
|
|
{
|
|
bool overwrite;
|
|
uint32_t len;
|
|
char *newname = NULL;
|
|
struct files_struct *dst_dirfsp = NULL;
|
|
struct smb_filename *smb_fname_dst = NULL;
|
|
const char *dst_original_lcomp = NULL;
|
|
uint32_t ucf_flags = ucf_flags_from_smb_request(req);
|
|
NTTIME dst_twrp = 0;
|
|
NTSTATUS status = NT_STATUS_OK;
|
|
bool is_dfs = (req->flags2 & FLAGS2_DFS_PATHNAMES);
|
|
TALLOC_CTX *ctx = talloc_tos();
|
|
|
|
if (!fsp) {
|
|
return NT_STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
if (total_data < 20) {
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
overwrite = (CVAL(pdata,0) ? True : False);
|
|
len = IVAL(pdata,16);
|
|
|
|
if (len > (total_data - 20) || (len == 0)) {
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
(void)srvstr_pull_talloc(ctx,
|
|
pdata,
|
|
req->flags2,
|
|
&newname,
|
|
&pdata[20],
|
|
len,
|
|
STR_TERMINATE);
|
|
|
|
if (newname == NULL) {
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
status = check_path_syntax_smb2(newname, is_dfs);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
|
|
DEBUG(10,("smb2_file_rename_information: got name |%s|\n",
|
|
newname));
|
|
|
|
if (newname[0] == ':') {
|
|
/* Create an smb_fname to call rename_internals_fsp() with. */
|
|
smb_fname_dst = synthetic_smb_fname(talloc_tos(),
|
|
fsp->base_fsp->fsp_name->base_name,
|
|
newname,
|
|
NULL,
|
|
fsp->base_fsp->fsp_name->twrp,
|
|
fsp->base_fsp->fsp_name->flags);
|
|
if (smb_fname_dst == NULL) {
|
|
status = NT_STATUS_NO_MEMORY;
|
|
goto out;
|
|
}
|
|
} else {
|
|
if (ucf_flags & UCF_GMT_PATHNAME) {
|
|
extract_snapshot_token(newname, &dst_twrp);
|
|
}
|
|
status = filename_convert_dirfsp(ctx,
|
|
conn,
|
|
newname,
|
|
ucf_flags,
|
|
dst_twrp,
|
|
&dst_dirfsp,
|
|
&smb_fname_dst);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Set the original last component, since
|
|
* rename_internals_fsp() requires it.
|
|
*/
|
|
dst_original_lcomp = get_original_lcomp(smb_fname_dst,
|
|
conn,
|
|
newname,
|
|
ucf_flags);
|
|
if (dst_original_lcomp == NULL) {
|
|
status = NT_STATUS_NO_MEMORY;
|
|
goto out;
|
|
}
|
|
|
|
DEBUG(10,("smb2_file_rename_information: "
|
|
"SMB_FILE_RENAME_INFORMATION (%s) %s -> %s\n",
|
|
fsp_fnum_dbg(fsp), fsp_str_dbg(fsp),
|
|
smb_fname_str_dbg(smb_fname_dst)));
|
|
status = rename_internals_fsp(conn,
|
|
fsp,
|
|
NULL, /* dst_dirfsp */
|
|
smb_fname_dst,
|
|
dst_original_lcomp,
|
|
(FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM),
|
|
overwrite);
|
|
|
|
out:
|
|
TALLOC_FREE(smb_fname_dst);
|
|
return status;
|
|
}
|
|
|
|
static NTSTATUS smb_file_link_information(connection_struct *conn,
|
|
struct smb_request *req,
|
|
const char *pdata,
|
|
int total_data,
|
|
files_struct *fsp,
|
|
struct smb_filename *smb_fname_src)
|
|
{
|
|
bool overwrite;
|
|
uint32_t len;
|
|
char *newname = NULL;
|
|
struct files_struct *dst_dirfsp = NULL;
|
|
struct smb_filename *smb_fname_dst = NULL;
|
|
NTSTATUS status = NT_STATUS_OK;
|
|
uint32_t ucf_flags = ucf_flags_from_smb_request(req);
|
|
NTTIME dst_twrp = 0;
|
|
TALLOC_CTX *ctx = talloc_tos();
|
|
|
|
if (!fsp) {
|
|
return NT_STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
if (total_data < 20) {
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
overwrite = (CVAL(pdata,0) ? true : false);
|
|
len = IVAL(pdata,16);
|
|
|
|
if (len > (total_data - 20) || (len == 0)) {
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (smb_fname_src->flags & SMB_FILENAME_POSIX_PATH) {
|
|
srvstr_get_path_posix(ctx,
|
|
pdata,
|
|
req->flags2,
|
|
&newname,
|
|
&pdata[20],
|
|
len,
|
|
STR_TERMINATE,
|
|
&status);
|
|
ucf_flags |= UCF_POSIX_PATHNAMES;
|
|
} else {
|
|
srvstr_get_path(ctx,
|
|
pdata,
|
|
req->flags2,
|
|
&newname,
|
|
&pdata[20],
|
|
len,
|
|
STR_TERMINATE,
|
|
&status);
|
|
}
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
|
|
DEBUG(10,("smb_file_link_information: got name |%s|\n",
|
|
newname));
|
|
|
|
if (ucf_flags & UCF_GMT_PATHNAME) {
|
|
extract_snapshot_token(newname, &dst_twrp);
|
|
}
|
|
status = filename_convert_dirfsp(ctx,
|
|
conn,
|
|
newname,
|
|
ucf_flags,
|
|
dst_twrp,
|
|
&dst_dirfsp,
|
|
&smb_fname_dst);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
|
|
if (fsp->base_fsp) {
|
|
/* No stream names. */
|
|
return NT_STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
DEBUG(10,("smb_file_link_information: "
|
|
"SMB_FILE_LINK_INFORMATION (%s) %s -> %s\n",
|
|
fsp_fnum_dbg(fsp), fsp_str_dbg(fsp),
|
|
smb_fname_str_dbg(smb_fname_dst)));
|
|
status = hardlink_internals(ctx,
|
|
conn,
|
|
req,
|
|
overwrite,
|
|
NULL, /* src_dirfsp */
|
|
fsp->fsp_name,
|
|
dst_dirfsp, /* dst_dirfsp */
|
|
smb_fname_dst);
|
|
|
|
TALLOC_FREE(smb_fname_dst);
|
|
return status;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Deal with SMB_FILE_RENAME_INFORMATION.
|
|
****************************************************************************/
|
|
|
|
static NTSTATUS smb_file_rename_information(connection_struct *conn,
|
|
struct smb_request *req,
|
|
const char *pdata,
|
|
int total_data,
|
|
files_struct *fsp,
|
|
struct smb_filename *smb_fname_src)
|
|
{
|
|
bool overwrite;
|
|
uint32_t root_fid;
|
|
uint32_t len;
|
|
char *newname = NULL;
|
|
struct files_struct *dst_dirfsp = NULL;
|
|
struct smb_filename *smb_fname_dst = NULL;
|
|
const char *dst_original_lcomp = NULL;
|
|
NTSTATUS status = NT_STATUS_OK;
|
|
char *p;
|
|
TALLOC_CTX *ctx = talloc_tos();
|
|
|
|
if (total_data < 13) {
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
overwrite = (CVAL(pdata,0) != 0);
|
|
root_fid = IVAL(pdata,4);
|
|
len = IVAL(pdata,8);
|
|
|
|
if (len > (total_data - 12) || (len == 0) || (root_fid != 0)) {
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (req->posix_pathnames) {
|
|
srvstr_get_path_posix(ctx,
|
|
pdata,
|
|
req->flags2,
|
|
&newname,
|
|
&pdata[12],
|
|
len,
|
|
0,
|
|
&status);
|
|
} else {
|
|
srvstr_get_path(ctx,
|
|
pdata,
|
|
req->flags2,
|
|
&newname,
|
|
&pdata[12],
|
|
len,
|
|
0,
|
|
&status);
|
|
}
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
|
|
DEBUG(10,("smb_file_rename_information: got name |%s|\n",
|
|
newname));
|
|
|
|
/* Check the new name has no '/' characters. */
|
|
if (strchr_m(newname, '/')) {
|
|
return NT_STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (fsp && fsp->base_fsp) {
|
|
/* newname must be a stream name. */
|
|
if (newname[0] != ':') {
|
|
return NT_STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
/* Create an smb_fname to call rename_internals_fsp() with. */
|
|
smb_fname_dst = synthetic_smb_fname(talloc_tos(),
|
|
fsp->base_fsp->fsp_name->base_name,
|
|
newname,
|
|
NULL,
|
|
fsp->base_fsp->fsp_name->twrp,
|
|
fsp->base_fsp->fsp_name->flags);
|
|
if (smb_fname_dst == NULL) {
|
|
status = NT_STATUS_NO_MEMORY;
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* Get the original last component, since
|
|
* rename_internals_fsp() requires it.
|
|
*/
|
|
dst_original_lcomp = get_original_lcomp(smb_fname_dst,
|
|
conn,
|
|
newname,
|
|
0);
|
|
if (dst_original_lcomp == NULL) {
|
|
status = NT_STATUS_NO_MEMORY;
|
|
goto out;
|
|
}
|
|
|
|
} else {
|
|
/*
|
|
* Build up an smb_fname_dst based on the filename passed in.
|
|
* We basically just strip off the last component, and put on
|
|
* the newname instead.
|
|
*/
|
|
char *base_name = NULL;
|
|
uint32_t ucf_flags = ucf_flags_from_smb_request(req);
|
|
NTTIME dst_twrp = 0;
|
|
|
|
/* newname must *not* be a stream name. */
|
|
if (newname[0] == ':') {
|
|
return NT_STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
/*
|
|
* Strip off the last component (filename) of the path passed
|
|
* in.
|
|
*/
|
|
base_name = talloc_strdup(ctx, smb_fname_src->base_name);
|
|
if (!base_name) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
p = strrchr_m(base_name, '/');
|
|
if (p) {
|
|
p[1] = '\0';
|
|
} else {
|
|
base_name = talloc_strdup(ctx, "");
|
|
if (!base_name) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
}
|
|
/* Append the new name. */
|
|
base_name = talloc_asprintf_append(base_name,
|
|
"%s",
|
|
newname);
|
|
if (!base_name) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
if (ucf_flags & UCF_GMT_PATHNAME) {
|
|
extract_snapshot_token(base_name, &dst_twrp);
|
|
}
|
|
status = filename_convert_dirfsp(ctx,
|
|
conn,
|
|
base_name,
|
|
ucf_flags,
|
|
dst_twrp,
|
|
&dst_dirfsp,
|
|
&smb_fname_dst);
|
|
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
goto out;
|
|
}
|
|
dst_original_lcomp = get_original_lcomp(smb_fname_dst,
|
|
conn,
|
|
newname,
|
|
ucf_flags);
|
|
if (dst_original_lcomp == NULL) {
|
|
status = NT_STATUS_NO_MEMORY;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if (fsp != NULL && fsp->fsp_flags.is_fsa) {
|
|
DEBUG(10,("smb_file_rename_information: "
|
|
"SMB_FILE_RENAME_INFORMATION (%s) %s -> %s\n",
|
|
fsp_fnum_dbg(fsp), fsp_str_dbg(fsp),
|
|
smb_fname_str_dbg(smb_fname_dst)));
|
|
status = rename_internals_fsp(conn,
|
|
fsp,
|
|
dst_dirfsp,
|
|
smb_fname_dst,
|
|
dst_original_lcomp,
|
|
0,
|
|
overwrite);
|
|
} else {
|
|
DEBUG(10,("smb_file_rename_information: "
|
|
"SMB_FILE_RENAME_INFORMATION %s -> %s\n",
|
|
smb_fname_str_dbg(smb_fname_src),
|
|
smb_fname_str_dbg(smb_fname_dst)));
|
|
status = rename_internals(ctx,
|
|
conn,
|
|
req,
|
|
NULL, /* src_dirfsp */
|
|
smb_fname_src,
|
|
dst_dirfsp,
|
|
smb_fname_dst,
|
|
dst_original_lcomp,
|
|
0,
|
|
overwrite,
|
|
FILE_WRITE_ATTRIBUTES);
|
|
}
|
|
out:
|
|
TALLOC_FREE(smb_fname_dst);
|
|
return status;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Deal with SMB_SET_FILE_BASIC_INFO.
|
|
****************************************************************************/
|
|
|
|
static NTSTATUS smb_set_file_basic_info(connection_struct *conn,
|
|
const char *pdata,
|
|
int total_data,
|
|
files_struct *fsp,
|
|
struct smb_filename *smb_fname)
|
|
{
|
|
/* Patch to do this correctly from Paul Eggert <eggert@twinsun.com>. */
|
|
struct smb_file_time ft;
|
|
uint32_t dosmode = 0;
|
|
NTSTATUS status = NT_STATUS_OK;
|
|
|
|
init_smb_file_time(&ft);
|
|
|
|
if (total_data < 36) {
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (fsp == NULL) {
|
|
return NT_STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
status = check_access_fsp(fsp, FILE_WRITE_ATTRIBUTES);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
|
|
/* Set the attributes */
|
|
dosmode = IVAL(pdata,32);
|
|
status = smb_set_file_dosmode(conn, fsp, dosmode);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
|
|
/* create time */
|
|
ft.create_time = pull_long_date_full_timespec(pdata);
|
|
|
|
/* access time */
|
|
ft.atime = pull_long_date_full_timespec(pdata+8);
|
|
|
|
/* write time. */
|
|
ft.mtime = pull_long_date_full_timespec(pdata+16);
|
|
|
|
/* change time. */
|
|
ft.ctime = pull_long_date_full_timespec(pdata+24);
|
|
|
|
DEBUG(10, ("smb_set_file_basic_info: file %s\n",
|
|
smb_fname_str_dbg(smb_fname)));
|
|
|
|
status = smb_set_file_time(conn, fsp, smb_fname, &ft, true);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
|
|
if (fsp->fsp_flags.modified) {
|
|
trigger_write_time_update_immediate(fsp);
|
|
}
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Deal with SMB_INFO_STANDARD.
|
|
****************************************************************************/
|
|
|
|
static NTSTATUS smb_set_info_standard(connection_struct *conn,
|
|
const char *pdata,
|
|
int total_data,
|
|
files_struct *fsp,
|
|
struct smb_filename *smb_fname)
|
|
{
|
|
NTSTATUS status;
|
|
struct smb_file_time ft;
|
|
|
|
init_smb_file_time(&ft);
|
|
|
|
if (total_data < 12) {
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (fsp == NULL) {
|
|
return NT_STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
/* create time */
|
|
ft.create_time = time_t_to_full_timespec(srv_make_unix_date2(pdata));
|
|
/* access time */
|
|
ft.atime = time_t_to_full_timespec(srv_make_unix_date2(pdata+4));
|
|
/* write time */
|
|
ft.mtime = time_t_to_full_timespec(srv_make_unix_date2(pdata+8));
|
|
|
|
DEBUG(10,("smb_set_info_standard: file %s\n",
|
|
smb_fname_str_dbg(smb_fname)));
|
|
|
|
status = check_access_fsp(fsp, FILE_WRITE_ATTRIBUTES);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
|
|
status = smb_set_file_time(conn, fsp, smb_fname, &ft, true);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
|
|
if (fsp->fsp_flags.modified) {
|
|
trigger_write_time_update_immediate(fsp);
|
|
}
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Deal with SMB_SET_FILE_ALLOCATION_INFO.
|
|
****************************************************************************/
|
|
|
|
static NTSTATUS smb_set_file_allocation_info(connection_struct *conn,
|
|
struct smb_request *req,
|
|
const char *pdata,
|
|
int total_data,
|
|
files_struct *fsp,
|
|
struct smb_filename *smb_fname)
|
|
{
|
|
uint64_t allocation_size = 0;
|
|
NTSTATUS status = NT_STATUS_OK;
|
|
files_struct *new_fsp = NULL;
|
|
|
|
if (!VALID_STAT(smb_fname->st)) {
|
|
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
|
|
if (total_data < 8) {
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
allocation_size = (uint64_t)IVAL(pdata,0);
|
|
allocation_size |= (((uint64_t)IVAL(pdata,4)) << 32);
|
|
DEBUG(10,("smb_set_file_allocation_info: Set file allocation info for "
|
|
"file %s to %.0f\n", smb_fname_str_dbg(smb_fname),
|
|
(double)allocation_size));
|
|
|
|
if (allocation_size) {
|
|
allocation_size = smb_roundup(conn, allocation_size);
|
|
}
|
|
|
|
DEBUG(10,("smb_set_file_allocation_info: file %s : setting new "
|
|
"allocation size to %.0f\n", smb_fname_str_dbg(smb_fname),
|
|
(double)allocation_size));
|
|
|
|
if (fsp &&
|
|
!fsp->fsp_flags.is_pathref &&
|
|
fsp_get_io_fd(fsp) != -1)
|
|
{
|
|
/* Open file handle. */
|
|
if (!(fsp->access_mask & FILE_WRITE_DATA)) {
|
|
return NT_STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
/* Only change if needed. */
|
|
if (allocation_size != get_file_size_stat(&smb_fname->st)) {
|
|
if (vfs_allocate_file_space(fsp, allocation_size) == -1) {
|
|
return map_nt_error_from_unix(errno);
|
|
}
|
|
}
|
|
/* But always update the time. */
|
|
/*
|
|
* This is equivalent to a write. Ensure it's seen immediately
|
|
* if there are no pending writes.
|
|
*/
|
|
trigger_write_time_update_immediate(fsp);
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
/* Pathname or stat or directory file. */
|
|
status = SMB_VFS_CREATE_FILE(
|
|
conn, /* conn */
|
|
req, /* req */
|
|
NULL, /* dirfsp */
|
|
smb_fname, /* fname */
|
|
FILE_WRITE_DATA, /* access_mask */
|
|
(FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
|
|
FILE_SHARE_DELETE),
|
|
FILE_OPEN, /* create_disposition*/
|
|
0, /* create_options */
|
|
FILE_ATTRIBUTE_NORMAL, /* file_attributes */
|
|
0, /* oplock_request */
|
|
NULL, /* lease */
|
|
0, /* allocation_size */
|
|
0, /* private_flags */
|
|
NULL, /* sd */
|
|
NULL, /* ea_list */
|
|
&new_fsp, /* result */
|
|
NULL, /* pinfo */
|
|
NULL, NULL); /* create context */
|
|
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
/* NB. We check for open_was_deferred in the caller. */
|
|
return status;
|
|
}
|
|
|
|
/* Only change if needed. */
|
|
if (allocation_size != get_file_size_stat(&smb_fname->st)) {
|
|
if (vfs_allocate_file_space(new_fsp, allocation_size) == -1) {
|
|
status = map_nt_error_from_unix(errno);
|
|
close_file_free(req, &new_fsp, NORMAL_CLOSE);
|
|
return status;
|
|
}
|
|
}
|
|
|
|
/* Changing the allocation size should set the last mod time. */
|
|
/*
|
|
* This is equivalent to a write. Ensure it's seen immediately
|
|
* if there are no pending writes.
|
|
*/
|
|
trigger_write_time_update_immediate(new_fsp);
|
|
close_file_free(req, &new_fsp, NORMAL_CLOSE);
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Deal with SMB_SET_FILE_END_OF_FILE_INFO.
|
|
****************************************************************************/
|
|
|
|
static NTSTATUS smb_set_file_end_of_file_info(connection_struct *conn,
|
|
struct smb_request *req,
|
|
const char *pdata,
|
|
int total_data,
|
|
files_struct *fsp,
|
|
struct smb_filename *smb_fname,
|
|
bool fail_after_createfile)
|
|
{
|
|
off_t size;
|
|
|
|
if (total_data < 8) {
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
size = IVAL(pdata,0);
|
|
size |= (((off_t)IVAL(pdata,4)) << 32);
|
|
DEBUG(10,("smb_set_file_end_of_file_info: Set end of file info for "
|
|
"file %s to %.0f\n", smb_fname_str_dbg(smb_fname),
|
|
(double)size));
|
|
|
|
return smb_set_file_size(conn, req,
|
|
fsp,
|
|
smb_fname,
|
|
&smb_fname->st,
|
|
size,
|
|
fail_after_createfile);
|
|
}
|
|
|
|
NTSTATUS smbd_do_setfilepathinfo(connection_struct *conn,
|
|
struct smb_request *req,
|
|
TALLOC_CTX *mem_ctx,
|
|
uint16_t info_level,
|
|
files_struct *fsp,
|
|
struct smb_filename *smb_fname,
|
|
char **ppdata, int total_data,
|
|
int *ret_data_size)
|
|
{
|
|
char *pdata = *ppdata;
|
|
NTSTATUS status = NT_STATUS_OK;
|
|
int data_return_size = 0;
|
|
|
|
*ret_data_size = 0;
|
|
|
|
DEBUG(3,("smbd_do_setfilepathinfo: %s (%s) info_level=%d "
|
|
"totdata=%d\n", smb_fname_str_dbg(smb_fname),
|
|
fsp_fnum_dbg(fsp),
|
|
info_level, total_data));
|
|
|
|
switch (info_level) {
|
|
|
|
case SMB_INFO_STANDARD:
|
|
{
|
|
status = smb_set_info_standard(conn,
|
|
pdata,
|
|
total_data,
|
|
fsp,
|
|
smb_fname);
|
|
break;
|
|
}
|
|
|
|
case SMB_INFO_SET_EA:
|
|
{
|
|
status = smb_info_set_ea(conn,
|
|
pdata,
|
|
total_data,
|
|
fsp,
|
|
smb_fname);
|
|
break;
|
|
}
|
|
|
|
case SMB_SET_FILE_BASIC_INFO:
|
|
case SMB_FILE_BASIC_INFORMATION:
|
|
{
|
|
status = smb_set_file_basic_info(conn,
|
|
pdata,
|
|
total_data,
|
|
fsp,
|
|
smb_fname);
|
|
break;
|
|
}
|
|
|
|
case SMB_FILE_ALLOCATION_INFORMATION:
|
|
case SMB_SET_FILE_ALLOCATION_INFO:
|
|
{
|
|
status = smb_set_file_allocation_info(conn, req,
|
|
pdata,
|
|
total_data,
|
|
fsp,
|
|
smb_fname);
|
|
break;
|
|
}
|
|
|
|
case SMB_FILE_END_OF_FILE_INFORMATION:
|
|
case SMB_SET_FILE_END_OF_FILE_INFO:
|
|
{
|
|
/*
|
|
* XP/Win7 both fail after the createfile with
|
|
* SMB_SET_FILE_END_OF_FILE_INFO but not
|
|
* SMB_FILE_END_OF_FILE_INFORMATION (pass-through).
|
|
* The level is known here, so pass it down
|
|
* appropriately.
|
|
*/
|
|
bool should_fail =
|
|
(info_level == SMB_SET_FILE_END_OF_FILE_INFO);
|
|
|
|
status = smb_set_file_end_of_file_info(conn, req,
|
|
pdata,
|
|
total_data,
|
|
fsp,
|
|
smb_fname,
|
|
should_fail);
|
|
break;
|
|
}
|
|
|
|
case SMB_FILE_DISPOSITION_INFORMATION:
|
|
case SMB_SET_FILE_DISPOSITION_INFO: /* Set delete on close for open file. */
|
|
{
|
|
#if 0
|
|
/* JRA - We used to just ignore this on a path ?
|
|
* Shouldn't this be invalid level on a pathname
|
|
* based call ?
|
|
*/
|
|
if (tran_call != TRANSACT2_SETFILEINFO) {
|
|
return ERROR_NT(NT_STATUS_INVALID_LEVEL);
|
|
}
|
|
#endif
|
|
status = smb_set_file_disposition_info(conn,
|
|
pdata,
|
|
total_data,
|
|
fsp,
|
|
smb_fname);
|
|
break;
|
|
}
|
|
|
|
case SMB_FILE_POSITION_INFORMATION:
|
|
{
|
|
status = smb_file_position_information(conn,
|
|
pdata,
|
|
total_data,
|
|
fsp);
|
|
break;
|
|
}
|
|
|
|
case SMB_FILE_FULL_EA_INFORMATION:
|
|
{
|
|
status = smb_set_file_full_ea_info(conn,
|
|
pdata,
|
|
total_data,
|
|
fsp);
|
|
break;
|
|
}
|
|
|
|
/* From tridge Samba4 :
|
|
* MODE_INFORMATION in setfileinfo (I have no
|
|
* idea what "mode information" on a file is - it takes a value of 0,
|
|
* 2, 4 or 6. What could it be?).
|
|
*/
|
|
|
|
case SMB_FILE_MODE_INFORMATION:
|
|
{
|
|
status = smb_file_mode_information(conn,
|
|
pdata,
|
|
total_data);
|
|
break;
|
|
}
|
|
|
|
/* [MS-SMB2] 3.3.5.21.1 states we MUST fail with STATUS_NOT_SUPPORTED. */
|
|
case SMB_FILE_VALID_DATA_LENGTH_INFORMATION:
|
|
case SMB_FILE_SHORT_NAME_INFORMATION:
|
|
return NT_STATUS_NOT_SUPPORTED;
|
|
|
|
case SMB_FILE_RENAME_INFORMATION:
|
|
{
|
|
status = smb_file_rename_information(conn, req,
|
|
pdata, total_data,
|
|
fsp, smb_fname);
|
|
break;
|
|
}
|
|
|
|
case SMB2_FILE_RENAME_INFORMATION_INTERNAL:
|
|
{
|
|
/* SMB2 rename information. */
|
|
status = smb2_file_rename_information(conn, req,
|
|
pdata, total_data,
|
|
fsp, smb_fname);
|
|
break;
|
|
}
|
|
|
|
case SMB_FILE_LINK_INFORMATION:
|
|
{
|
|
status = smb_file_link_information(conn, req,
|
|
pdata, total_data,
|
|
fsp, smb_fname);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
return NT_STATUS_INVALID_LEVEL;
|
|
}
|
|
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
|
|
*ret_data_size = data_return_size;
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
static uint32_t generate_volume_serial_number(
|
|
const struct loadparm_substitution *lp_sub,
|
|
int snum)
|
|
{
|
|
int serial = lp_volume_serial_number(snum);
|
|
return serial != -1 ? serial:
|
|
str_checksum(lp_servicename(talloc_tos(), lp_sub, snum)) ^
|
|
(str_checksum(get_local_machine_name())<<16);
|
|
}
|