1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-22 13:34:15 +03:00

smbd: Simplify openat_pathref_fsp_case_insensitive()

This is more lines of code, but it's still a simplification. With this
patch we don't call the full openat_pathref_fsp() anymore when looking
up the last component in filename_convert_dirfsp(), instead we do the
direct SMB_VFS_OPENAT(). We don't need the whole complexity of
non_widelink_open() for this case, we do know that we have a real
non-cwd dirfsp.

The other big change that is not obvious just from looking at the
patch: This removes the special case for looking up posix
symlinks. Before this patch, filename_convert_dirfsp() returned a
proper smb_filename but without an attached fsp when a smb1 posix
client hits a symlink. This caused all sorts of special case code
everywhere. For example smbd_do_qfilepathinfo() needs to cover both
cases just for the smb1 posix symlink case. This special-case handling
can go now. We can do the path lookup in the smb1-only qpathinfo code
and call into the common code with a proper fsp.

When hitting a symlink and with O_PATH available, we'll get the
symlink opened with an O_PATH fd. Without O_PATH we obviously can't do
that, there we get fd=-1 and an indication that we don't have the
procfd fallback around.

Why all this?

I want to present FIFOs (and eventually symlinks) as reparse points as
the very next step. Without this patch, there is no real unified way
to get the file attributes from disk. Now we can use the proper logic
of fdos_mode() everywhere and not rely on special cases for fsp==NULL.

This patch also changes some error codes for smb1 posix extensions. I
chose to just change the test instead of going after each and every
change. As long as we do get an error, I'm willing to accept that we
slightly change error path behaviour for this deprecated code.

And, I tried to split this up into smaller patches but I failed.

Signed-off-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
This commit is contained in:
Volker Lendecke 2023-10-18 11:50:20 +02:00
parent ab56379c22
commit 8d00b0e664
7 changed files with 385 additions and 243 deletions

View File

@ -389,7 +389,7 @@ static int widelinks_openat(vfs_handle_struct *handle,
fsp->fsp_name->st = full_fname->st;
}
TALLOC_FREE(full_fname);
errno = ENOENT;
errno = ELOOP;
}
return ret;
}

View File

@ -194,13 +194,13 @@ test_symlink_traversal_SMB1_posix_onename()
# ls commands.
#
smbclient_expect_error "ls" "$name" "" "NT_STATUS_OK" || return 1
smbclient_expect_error "ls" "$name/noexist" "" "NT_STATUS_NOT_A_DIRECTORY" || return 1
smbclient_expect_error "ls" "$name/*" "" "NT_STATUS_NOT_A_DIRECTORY" || return 1
smbclient_expect_error "ls" "$name/noexist" "" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
smbclient_expect_error "ls" "$name/*" "" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
smbclient_expect_error "ls" "$name/*/noexist" "" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
# Now in subdirectory emptydir
smbclient_expect_error "ls" "emptydir/$name" "" "NT_STATUS_OK" || return 1
smbclient_expect_error "ls" "emptydir/$name/noexist" "" "NT_STATUS_NOT_A_DIRECTORY" || return 1
smbclient_expect_error "ls" "emptydir/$name/*" "" "NT_STATUS_NOT_A_DIRECTORY" || return 1
smbclient_expect_error "ls" "emptydir/$name/noexist" "" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
smbclient_expect_error "ls" "emptydir/$name/*" "" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
smbclient_expect_error "ls" "emptydir/$name/*/noexist" "" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
#
# SMB1+POSIX stat commands. All symlinks can be stat'ed.
@ -211,9 +211,9 @@ test_symlink_traversal_SMB1_posix_onename()
# del commands. Under SMB1+POSIX we can legitimately delete symlinks, so don't
# try and delete symlink targets, we need them for the later tests.
#
smbclient_expect_error "del" "$name/noexist" "" "NT_STATUS_NOT_A_DIRECTORY" || return 1
smbclient_expect_error "del" "$name/noexist" "" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
# Now in subdirectory emptydir
smbclient_expect_error "del" "emptydir/$name/noexist" "" "NT_STATUS_NOT_A_DIRECTORY" || return 1
smbclient_expect_error "del" "emptydir/$name/noexist" "" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
if [ "$do_rename" = "do rename" ]; then
#

View File

@ -29,7 +29,6 @@
#include "fake_file.h"
#include "smbd/smbd.h"
#include "smbd/globals.h"
#include "lib/util/memcache.h"
#include "libcli/smb/reparse.h"
uint32_t ucf_flags_from_smb_request(struct smb_request *req)
@ -640,155 +639,6 @@ static NTSTATUS filename_convert_normalize_new(
return NT_STATUS_OK;
}
/*
* Open smb_fname_rel->fsp as a pathref fsp with a case insensitive
* fallback using GETREALFILENAME_CACHE and get_real_filename_at() if
* the first attempt based on the filename sent by the client gives
* ENOENT.
*/
static NTSTATUS openat_pathref_fsp_case_insensitive(
struct files_struct *dirfsp,
struct smb_filename *smb_fname_rel,
uint32_t ucf_flags)
{
const bool posix = (ucf_flags & UCF_POSIX_PATHNAMES);
DATA_BLOB cache_key = { .data = NULL, };
char *found_name = NULL;
NTSTATUS status;
bool ok;
SET_STAT_INVALID(smb_fname_rel->st);
/* Check veto files - only looks at last component. */
if (IS_VETO_PATH(dirfsp->conn, smb_fname_rel->base_name)) {
DBG_DEBUG("veto files rejecting last component %s\n",
smb_fname_str_dbg(smb_fname_rel));
return NT_STATUS_NETWORK_OPEN_RESTRICTION;
}
status = openat_pathref_fsp(dirfsp, smb_fname_rel);
if (NT_STATUS_IS_OK(status)) {
return NT_STATUS_OK;
}
if (VALID_STAT(smb_fname_rel->st)) {
/*
* We got an error although the object existed. Might
* be a symlink we don't want.
*/
return status;
}
if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
/*
* Only retry on ENOENT
*/
return status;
}
if (posix || dirfsp->conn->case_sensitive) {
/*
* Only return case insensitive if required
*/
return status;
}
if (lp_stat_cache()) {
char *base_name = smb_fname_rel->base_name;
char *original_relname = NULL;
DATA_BLOB value = { .data = NULL };
ok = get_real_filename_cache_key(
talloc_tos(), dirfsp, base_name, &cache_key);
if (!ok) {
/*
* probably ENOMEM, just bail
*/
return status;
}
DO_PROFILE_INC(statcache_lookups);
ok = memcache_lookup(
NULL, GETREALFILENAME_CACHE, cache_key, &value);
if (!ok) {
DO_PROFILE_INC(statcache_misses);
goto lookup;
}
DO_PROFILE_INC(statcache_hits);
/*
* For the "new filename" case we need to preserve the
* capitalization the client sent us, see
* https://bugzilla.samba.org/show_bug.cgi?id=15481
*/
original_relname = smb_fname_rel->base_name;
smb_fname_rel->base_name = talloc_memdup(
smb_fname_rel, value.data, value.length);
if (smb_fname_rel->base_name == NULL) {
TALLOC_FREE(cache_key.data);
return NT_STATUS_NO_MEMORY;
}
if (IS_VETO_PATH(dirfsp->conn, smb_fname_rel->base_name)) {
DBG_DEBUG("veto files rejecting last component %s\n",
smb_fname_str_dbg(smb_fname_rel));
TALLOC_FREE(cache_key.data);
return NT_STATUS_NETWORK_OPEN_RESTRICTION;
}
status = openat_pathref_fsp(dirfsp, smb_fname_rel);
if (NT_STATUS_IS_OK(status)) {
TALLOC_FREE(cache_key.data);
TALLOC_FREE(original_relname);
return NT_STATUS_OK;
}
memcache_delete(NULL, GETREALFILENAME_CACHE, cache_key);
TALLOC_FREE(smb_fname_rel->base_name);
smb_fname_rel->base_name = original_relname;
}
lookup:
status = get_real_filename_at(
dirfsp, smb_fname_rel->base_name, smb_fname_rel, &found_name);
if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) &&
(ucf_flags & UCF_PREP_CREATEFILE)) {
/*
* dropbox
*/
status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
if (NT_STATUS_IS_OK(status)) {
TALLOC_FREE(smb_fname_rel->base_name);
smb_fname_rel->base_name = found_name;
if (IS_VETO_PATH(dirfsp->conn, smb_fname_rel->base_name)) {
DBG_DEBUG("veto files rejecting last component %s\n",
smb_fname_str_dbg(smb_fname_rel));
return NT_STATUS_NETWORK_OPEN_RESTRICTION;
}
status = openat_pathref_fsp(dirfsp, smb_fname_rel);
}
if (NT_STATUS_IS_OK(status) && (cache_key.data != NULL)) {
DATA_BLOB value = {
.data = (uint8_t *)smb_fname_rel->base_name,
.length = strlen(smb_fname_rel->base_name) + 1,
};
memcache_add(NULL, GETREALFILENAME_CACHE, cache_key, value);
}
TALLOC_FREE(cache_key.data);
return status;
}
static const char *previous_slash(const char *name_in, const char *slash)
{
const char *prev = NULL;
@ -936,6 +786,7 @@ static NTSTATUS filename_convert_dirfsp_nosymlink(
struct smb_filename *smb_dirname = NULL;
struct smb_filename *smb_fname_rel = NULL;
struct smb_filename *smb_fname = NULL;
struct open_symlink_err *symlink_err = NULL;
const bool posix = (ucf_flags & UCF_POSIX_PATHNAMES);
char *dirname = NULL;
const char *fname_rel = NULL;
@ -1021,8 +872,6 @@ static NTSTATUS filename_convert_dirfsp_nosymlink(
posix ? SMB_FILENAME_POSIX_PATH : 0,
&smb_dirname);
} else {
struct open_symlink_err *symlink_err = NULL;
status = normalize_filename_case(conn, dirname, ucf_flags);
if (!NT_STATUS_IS_OK(status)) {
DBG_ERR("normalize_filename_case %s failed: %s\n",
@ -1043,14 +892,6 @@ static NTSTATUS filename_convert_dirfsp_nosymlink(
if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
size_t name_in_len, dirname_len;
if (lp_host_msdfs() && lp_msdfs_root(SNUM(conn)) &&
strnequal(symlink_err->reparse->substitute_name,
"msdfs:",
6)) {
status = NT_STATUS_PATH_NOT_COVERED;
goto fail;
}
name_in_len = strlen(name_in);
dirname_len = strlen(dirname);
@ -1143,45 +984,27 @@ static NTSTATUS filename_convert_dirfsp_nosymlink(
goto fail;
}
status = openat_pathref_fsp_case_insensitive(
smb_dirname->fsp, smb_fname_rel, ucf_flags);
status = openat_pathref_fsp_lcomp(smb_dirname->fsp,
smb_fname_rel,
ucf_flags);
if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) &&
VALID_STAT(smb_fname_rel->st) &&
S_ISLNK(smb_fname_rel->st.st_ex_mode)) {
if (NT_STATUS_IS_OK(status) && S_ISLNK(smb_fname_rel->st.st_ex_mode)) {
/*
* If we're on an MSDFS share, see if this is
* an MSDFS link.
* Upper layers might need the link target. Here we
* still have the relname around, get the symlink err.
*/
if (lp_host_msdfs() &&
lp_msdfs_root(SNUM(conn)) &&
is_msdfs_link(smb_dirname->fsp, smb_fname_rel))
{
status = NT_STATUS_PATH_NOT_COVERED;
status = create_open_symlink_err(mem_ctx,
smb_dirname->fsp,
smb_fname_rel,
&symlink_err);
if (!NT_STATUS_IS_OK(status)) {
DBG_DEBUG("Could not read symlink for %s: %s\n",
smb_fname_str_dbg(
smb_fname_rel->fsp->fsp_name),
nt_errstr(status));
goto fail;
}
#if defined(WITH_SMB1SERVER)
/*
* In SMB1 posix mode, if this is a symlink,
* allow access to the name with a NULL smb_fname->fsp.
*/
if (ucf_flags & UCF_LCOMP_LNK_OK) {
SMB_ASSERT(smb_fname_rel->fsp == NULL);
SMB_ASSERT(streamname == NULL);
smb_fname = full_path_from_dirfsp_atname(
mem_ctx,
smb_dirname->fsp,
smb_fname_rel);
if (smb_fname == NULL) {
status = NT_STATUS_NO_MEMORY;
goto fail;
}
goto done;
}
#endif
}
if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) &&
@ -1314,6 +1137,7 @@ static NTSTATUS filename_convert_dirfsp_nosymlink(
done:
*_dirfsp = smb_dirname->fsp;
*_smb_fname = smb_fname;
*_symlink_err = symlink_err;
smb_fname_fsp_unlink(smb_fname_rel);
TALLOC_FREE(smb_fname_rel);
@ -1366,10 +1190,34 @@ next:
_smb_fname,
&symlink_err);
if (NT_STATUS_IS_OK(status) && S_ISLNK((*_smb_fname)->st.st_ex_mode)) {
/*
* lcomp is a symlink
*/
if (ucf_flags & UCF_LCOMP_LNK_OK) {
TALLOC_FREE(symlink_err);
return NT_STATUS_OK;
}
close_file_free(NULL, _dirfsp, ERROR_CLOSE);
status = NT_STATUS_STOPPED_ON_SYMLINK;
}
if (!NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
return status;
}
/*
* If we're on an MSDFS share, see if this is
* an MSDFS link.
*/
if (lp_host_msdfs() && lp_msdfs_root(SNUM(conn)) &&
strnequal(symlink_err->reparse->substitute_name, "msdfs:", 6))
{
TALLOC_FREE(*_smb_fname);
TALLOC_FREE(symlink_err);
return NT_STATUS_PATH_NOT_COVERED;
}
if (!lp_follow_symlinks(SNUM(conn))) {
status = (symlink_err->unparsed == 0)
? NT_STATUS_OBJECT_NAME_NOT_FOUND

View File

@ -25,6 +25,7 @@
#include "util_tdb.h"
#include "lib/util/bitmap.h"
#include "lib/util/strv.h"
#include "lib/util/memcache.h"
#include "libcli/smb/reparse.h"
#define FILE_HANDLE_OFFSET 0x1000
@ -831,6 +832,135 @@ NTSTATUS create_open_symlink_err(TALLOC_CTX *mem_ctx,
return NT_STATUS_OK;
}
static int smb_vfs_openat_ci(TALLOC_CTX *mem_ctx,
bool case_sensitive,
struct connection_struct *conn,
struct files_struct *dirfsp,
struct smb_filename *smb_fname_rel,
files_struct *fsp,
const struct vfs_open_how *how)
{
char *orig_base_name = smb_fname_rel->base_name;
DATA_BLOB cache_key = {
.data = NULL,
};
DATA_BLOB cache_value = {
.data = NULL,
};
NTSTATUS status;
int fd;
bool ok;
fd = SMB_VFS_OPENAT(conn, dirfsp, smb_fname_rel, fsp, how);
if ((fd >= 0) || case_sensitive) {
return fd;
}
if (errno != ENOENT) {
return -1;
}
if (!lp_stat_cache()) {
goto lookup;
}
ok = get_real_filename_cache_key(mem_ctx,
dirfsp,
orig_base_name,
&cache_key);
if (!ok) {
/*
* probably ENOMEM, just bail
*/
errno = ENOMEM;
return -1;
}
DO_PROFILE_INC(statcache_lookups);
ok = memcache_lookup(NULL,
GETREALFILENAME_CACHE,
cache_key,
&cache_value);
if (!ok) {
DO_PROFILE_INC(statcache_misses);
goto lookup;
}
DO_PROFILE_INC(statcache_hits);
smb_fname_rel->base_name = talloc_strndup(mem_ctx,
(char *)cache_value.data,
cache_value.length);
if (smb_fname_rel->base_name == NULL) {
TALLOC_FREE(cache_key.data);
smb_fname_rel->base_name = orig_base_name;
errno = ENOMEM;
return -1;
}
if (IS_VETO_PATH(dirfsp->conn, smb_fname_rel->base_name)) {
DBG_DEBUG("veto files rejecting last component %s\n",
smb_fname_str_dbg(smb_fname_rel));
TALLOC_FREE(cache_key.data);
smb_fname_rel->base_name = orig_base_name;
errno = EPERM;
return -1;
}
fd = SMB_VFS_OPENAT(conn, dirfsp, smb_fname_rel, fsp, how);
if (fd >= 0) {
TALLOC_FREE(cache_key.data);
return fd;
}
memcache_delete(NULL, GETREALFILENAME_CACHE, cache_key);
/*
* For the "new filename" case we need to preserve the
* capitalization the client sent us, see
* https://bugzilla.samba.org/show_bug.cgi?id=15481
*/
TALLOC_FREE(smb_fname_rel->base_name);
smb_fname_rel->base_name = orig_base_name;
lookup:
status = get_real_filename_at(dirfsp,
orig_base_name,
mem_ctx,
&smb_fname_rel->base_name);
if (!NT_STATUS_IS_OK(status)) {
DBG_DEBUG("get_real_filename_at() failed: %s\n",
nt_errstr(status));
errno = ENOENT;
return -1;
}
if (IS_VETO_PATH(conn, smb_fname_rel->base_name)) {
DBG_DEBUG("found veto files path component "
"%s => %s\n",
orig_base_name,
smb_fname_rel->base_name);
TALLOC_FREE(smb_fname_rel->base_name);
smb_fname_rel->base_name = orig_base_name;
errno = ENOENT;
return -1;
}
fd = SMB_VFS_OPENAT(conn, dirfsp, smb_fname_rel, fsp, how);
if ((fd >= 0) && (cache_key.data != NULL)) {
DATA_BLOB value = {
.data = (uint8_t *)smb_fname_rel->base_name,
.length = strlen(smb_fname_rel->base_name) + 1,
};
memcache_add(NULL, GETREALFILENAME_CACHE, cache_key, value);
TALLOC_FREE(cache_key.data);
}
return fd;
}
NTSTATUS openat_pathref_fsp_nosymlink(TALLOC_CTX *mem_ctx,
struct connection_struct *conn,
struct files_struct *in_dirfsp,
@ -855,7 +985,7 @@ NTSTATUS openat_pathref_fsp_nosymlink(TALLOC_CTX *mem_ctx,
struct open_symlink_err *symlink_err = NULL;
struct files_struct *fsp = NULL;
char *path = NULL, *next = NULL;
bool case_sensitive, ok, is_toplevel;
bool ok, is_toplevel;
int fd;
NTSTATUS status;
struct vfs_open_how how = {
@ -1030,48 +1160,13 @@ NTSTATUS openat_pathref_fsp_nosymlink(TALLOC_CTX *mem_ctx,
next:
next = strv_next(path, rel_fname.base_name);
fd = SMB_VFS_OPENAT(
conn,
dirfsp,
&rel_fname,
fsp,
&how);
case_sensitive = (posix || conn->case_sensitive);
if ((fd == -1) && (errno == ENOENT) && !case_sensitive) {
const char *orig_base_name = rel_fname.base_name;
status = get_real_filename_at(
dirfsp,
rel_fname.base_name,
talloc_tos(),
&rel_fname.base_name);
if (!NT_STATUS_IS_OK(status)) {
DBG_DEBUG("get_real_filename_at failed: %s\n",
nt_errstr(status));
goto fail;
}
/* Name might have been demangled - check veto files. */
if (IS_VETO_PATH(conn, rel_fname.base_name)) {
DBG_DEBUG("%s contains veto files path component "
"%s => %s\n",
path_in,
orig_base_name,
rel_fname.base_name);
status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
goto fail;
}
fd = SMB_VFS_OPENAT(
conn,
dirfsp,
&rel_fname,
fsp,
&how);
}
fd = smb_vfs_openat_ci(talloc_tos(),
posix || conn->case_sensitive,
conn,
dirfsp,
&rel_fname,
fsp,
&how);
#ifndef O_PATH
if ((fd == -1) && (errno == ELOOP)) {
@ -1304,6 +1399,162 @@ fail:
return status;
}
/*
* Open smb_fname_rel->fsp as a pathref fsp with a case insensitive
* fallback using GETREALFILENAME_CACHE and get_real_filename_at() if
* the first attempt based on the filename sent by the client gives
* ENOENT.
*/
NTSTATUS openat_pathref_fsp_lcomp(struct files_struct *dirfsp,
struct smb_filename *smb_fname_rel,
uint32_t ucf_flags)
{
struct connection_struct *conn = dirfsp->conn;
const char *orig_rel_base_name = smb_fname_rel->base_name;
struct files_struct *fsp = NULL;
struct smb_filename *full_fname = NULL;
struct vfs_open_how how = {
.flags = O_RDONLY | O_NONBLOCK | O_NOFOLLOW,
};
NTSTATUS status;
int ret, fd;
/*
* Make sure we don't need of the all the magic in
* openat_pathref_fsp() with regards non_widelink_open etc.
*/
SMB_ASSERT((smb_fname_rel->fsp == NULL) &&
(dirfsp != dirfsp->conn->cwd_fsp) &&
(strchr_m(smb_fname_rel->base_name, '/') == NULL) &&
!is_named_stream(smb_fname_rel));
SET_STAT_INVALID(smb_fname_rel->st);
/* Check veto files - only looks at last component. */
if (IS_VETO_PATH(dirfsp->conn, smb_fname_rel->base_name)) {
DBG_DEBUG("veto files rejecting last component %s\n",
smb_fname_str_dbg(smb_fname_rel));
return NT_STATUS_NETWORK_OPEN_RESTRICTION;
}
status = fsp_new(conn, conn, &fsp);
if (!NT_STATUS_IS_OK(status)) {
DBG_DEBUG("fsp_new() failed: %s\n", nt_errstr(status));
return status;
}
GetTimeOfDay(&fsp->open_time);
fsp_set_gen_id(fsp);
ZERO_STRUCT(conn->sconn->fsp_fi_cache);
fsp->fsp_flags.is_pathref = true;
full_fname = full_path_from_dirfsp_atname(conn, dirfsp, smb_fname_rel);
if (full_fname == NULL) {
DBG_DEBUG("full_path_from_dirfsp_atname(%s/%s) failed\n",
dirfsp->fsp_name->base_name,
smb_fname_rel->base_name);
file_free(NULL, fsp);
return NT_STATUS_NO_MEMORY;
}
status = fsp_attach_smb_fname(fsp, &full_fname);
if (!NT_STATUS_IS_OK(status)) {
DBG_DEBUG("fsp_attach_smb_fname(fsp, %s) failed: %s\n",
smb_fname_str_dbg(full_fname),
nt_errstr(status));
file_free(NULL, fsp);
return status;
}
fd = smb_vfs_openat_ci(smb_fname_rel,
(ucf_flags & UCF_POSIX_PATHNAMES) ||
conn->case_sensitive,
conn,
dirfsp,
smb_fname_rel,
fsp,
&how);
if ((fd == -1) && (errno == ENOENT)) {
status = map_nt_error_from_unix(errno);
DBG_DEBUG("smb_vfs_openat(%s/%s) failed: %s\n",
dirfsp->fsp_name->base_name,
smb_fname_rel->base_name,
strerror(errno));
file_free(NULL, fsp);
return status;
}
if (smb_fname_rel->base_name != orig_rel_base_name) {
struct smb_filename new_fullname = *smb_fname_rel;
DBG_DEBUG("rel->base_name changed from %s to %s\n",
orig_rel_base_name,
smb_fname_rel->base_name);
new_fullname.base_name = full_path_from_dirfsp_at_basename(
talloc_tos(), dirfsp, new_fullname.base_name);
if (new_fullname.base_name == NULL) {
fd_close(fsp);
file_free(NULL, fsp);
return NT_STATUS_NO_MEMORY;
}
status = fsp_set_smb_fname(fsp, &new_fullname);
if (!NT_STATUS_IS_OK(status)) {
fd_close(fsp);
file_free(NULL, fsp);
return status;
}
}
fsp_set_fd(fsp, fd);
if (fd >= 0) {
ret = SMB_VFS_FSTAT(fsp, &fsp->fsp_name->st);
} else {
ret = SMB_VFS_FSTATAT(fsp->conn,
dirfsp,
smb_fname_rel,
&fsp->fsp_name->st,
AT_SYMLINK_NOFOLLOW);
}
if (ret == -1) {
status = map_nt_error_from_unix(errno);
DBG_DEBUG("SMB_VFS_%sSTAT(%s/%s) failed: %s\n",
(fd >= 0) ? "F" : "",
dirfsp->fsp_name->base_name,
smb_fname_rel->base_name,
nt_errstr(status));
fd_close(fsp);
file_free(NULL, fsp);
return status;
}
fsp->fsp_flags.is_directory = S_ISDIR(fsp->fsp_name->st.st_ex_mode);
fsp->file_id = vfs_file_id_from_sbuf(conn, &fsp->fsp_name->st);
smb_fname_rel->st = fsp->fsp_name->st;
status = fsp_smb_fname_link(fsp,
&smb_fname_rel->fsp_link,
&smb_fname_rel->fsp);
if (!NT_STATUS_IS_OK(status)) {
DBG_DEBUG("fsp_smb_fname_link() failed: %s\n",
nt_errstr(status));
fd_close(fsp);
file_free(NULL, fsp);
return status;
}
DBG_DEBUG("fsp [%s]: OK, fd=%d\n", fsp_str_dbg(fsp), fd);
talloc_set_destructor(smb_fname_rel, smb_fname_fsp_destructor);
return NT_STATUS_OK;
}
void smb_fname_fsp_unlink(struct smb_filename *smb_fname)
{
talloc_set_destructor(smb_fname, NULL);

View File

@ -5961,6 +5961,31 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
goto fail;
}
if (!(create_options & FILE_OPEN_REPARSE_POINT) &&
(smb_fname->fsp != NULL) && /* new files don't have an fsp */
VALID_STAT(smb_fname->fsp->fsp_name->st))
{
mode_t type = (smb_fname->fsp->fsp_name->st.st_ex_mode &
S_IFMT);
switch (type) {
case S_IFREG:
FALL_THROUGH;
case S_IFDIR:
break;
case S_IFLNK:
/*
* We should never get this far with a symlink
* "as such". Report as not existing.
*/
status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
goto fail;
default:
status = NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED;
goto fail;
}
}
if (req == NULL) {
oplock_request |= INTERNAL_OPEN_ONLY;
}

View File

@ -448,6 +448,9 @@ NTSTATUS openat_pathref_fsp_nosymlink(TALLOC_CTX *mem_ctx,
bool posix,
struct smb_filename **_smb_fname,
struct open_symlink_err **_symlink_err);
NTSTATUS openat_pathref_fsp_lcomp(struct files_struct *dirfsp,
struct smb_filename *smb_fname_rel,
uint32_t ucf_flags);
NTSTATUS readlink_talloc(
TALLOC_CTX *mem_ctx,
struct files_struct *dirfsp,

View File

@ -1136,6 +1136,21 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
&state->info,
&in_context_blobs,
state->out_context_blobs);
if (NT_STATUS_IS_OK(status) &&
!(state->in_create_options & FILE_OPEN_REPARSE_POINT))
{
mode_t mode = state->result->fsp_name->st.st_ex_mode;
if (!(S_ISREG(mode) || S_ISDIR(mode))) {
/*
* Only open files and dirs without
* FILE_OPEN_REPARSE_POINT
*/
close_file_free(smb1req, &state->result, ERROR_CLOSE);
status = NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED;
}
}
if (!NT_STATUS_IS_OK(status)) {
if (open_was_deferred(smb1req->xconn, smb1req->mid)) {
SMBPROFILE_IOBYTES_ASYNC_SET_IDLE(smb2req->profile);