Handle differing symlink errno's
****************************************************************************/
-static int link_errno_convert(int err)
+static NTSTATUS link_errno_convert(int err)
{
#if defined(ENOTSUP) && defined(OSF1)
/* handle special Tru64 errno */
if (err == EMLINK) {
err = ELOOP;
}
- return err;
+ if (err == ELOOP) {
+ return NT_STATUS_STOPPED_ON_SYMLINK;
+ }
+ return map_nt_error_from_unix(err);
}
-static int non_widelink_open(const struct files_struct *dirfsp,
+static NTSTATUS non_widelink_open(const struct files_struct *dirfsp,
files_struct *fsp,
struct smb_filename *smb_fname,
int flags,
Follow a symlink in userspace.
****************************************************************************/
-static int process_symlink_open(const struct files_struct *dirfsp,
+static NTSTATUS process_symlink_open(const struct files_struct *dirfsp,
files_struct *fsp,
struct smb_filename *smb_fname,
int flags,
struct connection_struct *conn = dirfsp->conn;
const char *conn_rootdir = NULL;
struct smb_filename conn_rootdir_fname;
- int fd = -1;
char *link_target = NULL;
int link_len = -1;
struct smb_filename *oldwd_fname = NULL;
struct smb_filename *resolved_fname = NULL;
char *resolved_name = NULL;
bool matched = false;
- int saved_errno = 0;
struct smb_filename *full_fname = NULL;
+ NTSTATUS status;
conn_rootdir = SMB_VFS_CONNECTPATH(conn, smb_fname);
if (conn_rootdir == NULL) {
- errno = ENOMEM;
- return -1;
+ return NT_STATUS_NO_MEMORY;
}
conn_rootdir_fname = (struct smb_filename) {
.base_name = discard_const_p(char, conn_rootdir),
*/
link_depth++;
if (link_depth >= 20) {
- errno = ELOOP;
+ status = NT_STATUS_STOPPED_ON_SYMLINK;
goto out;
}
/* Allocate space for the link target. */
link_target = talloc_array(talloc_tos(), char, PATH_MAX);
if (link_target == NULL) {
- errno = ENOMEM;
+ status = NT_STATUS_NO_MEMORY;
goto out;
}
/*
* Read the link target. We do this just to verify that smb_fname indeed
- * points at a symbolic link and return the SMB_VFS_READLINKAT() errno
+ * points at a symbolic link and return NT_STATUS_NOT_A_DIRECTORY
* and failure in case smb_fname is NOT a symlink.
*
* The caller needs this piece of information to distinguish two cases
link_target,
PATH_MAX - 1);
if (link_len == -1) {
+ status = NT_STATUS_INVALID_PARAMETER;
goto out;
}
full_fname = full_path_from_dirfsp_atname(
talloc_tos(), dirfsp, smb_fname);
if (full_fname == NULL) {
+ status = NT_STATUS_NO_MEMORY;
goto out;
}
/* Convert to an absolute path. */
resolved_fname = SMB_VFS_REALPATH(conn, talloc_tos(), full_fname);
if (resolved_fname == NULL) {
+ status = map_nt_error_from_unix(errno);
goto out;
}
resolved_name = resolved_fname->base_name;
resolved_name,
rootdir_len) == 0);
if (!matched) {
- errno = EACCES;
+ status = NT_STATUS_STOPPED_ON_SYMLINK;
goto out;
}
smb_fname->base_name = talloc_strdup(smb_fname,
&resolved_name[rootdir_len+1]);
} else {
- errno = EACCES;
+ status = NT_STATUS_STOPPED_ON_SYMLINK;
goto out;
}
if (smb_fname->base_name == NULL) {
- errno = ENOMEM;
+ status = NT_STATUS_NO_MEMORY;
goto out;
}
oldwd_fname = vfs_GetWd(talloc_tos(), dirfsp->conn);
if (oldwd_fname == NULL) {
+ status = map_nt_error_from_unix(errno);
goto out;
}
/* Ensure we operate from the root of the share. */
if (vfs_ChDir(conn, &conn_rootdir_fname) == -1) {
+ status = map_nt_error_from_unix(errno);
goto out;
}
* dirfsp anymore, we pass conn->cwd_fsp as dirfsp to
* non_widelink_open() to trigger the chdir(parentdir) logic.
*/
- fd = non_widelink_open(conn->cwd_fsp,
+ status = non_widelink_open(conn->cwd_fsp,
fsp,
smb_fname,
flags,
mode,
link_depth);
- if (fd == -1) {
- saved_errno = errno;
- }
out:
}
TALLOC_FREE(oldwd_fname);
}
- if (saved_errno != 0) {
- errno = saved_errno;
- }
- return fd;
+
+ return status;
}
/****************************************************************************
Non-widelink open.
****************************************************************************/
-static int non_widelink_open(const struct files_struct *dirfsp,
+static NTSTATUS non_widelink_open(const struct files_struct *dirfsp,
files_struct *fsp,
struct smb_filename *smb_fname,
int flags,
unsigned int link_depth)
{
struct connection_struct *conn = fsp->conn;
- NTSTATUS status;
+ NTSTATUS saved_status;
+ NTSTATUS status = NT_STATUS_OK;
int fd = -1;
struct smb_filename *orig_fsp_name = fsp->fsp_name;
struct smb_filename *smb_fname_rel = NULL;
- int saved_errno = 0;
struct smb_filename *oldwd_fname = NULL;
struct smb_filename *parent_dir_fname = NULL;
bool have_opath = false;
if (fsp->fsp_flags.is_directory) {
parent_dir_fname = cp_smb_filename(talloc_tos(), smb_fname);
if (parent_dir_fname == NULL) {
- saved_errno = errno;
+ status = NT_STATUS_NO_MEMORY;
goto out;
}
smb_fname->twrp,
smb_fname->flags);
if (smb_fname_rel == NULL) {
- saved_errno = errno;
+ status = NT_STATUS_NO_MEMORY;
goto out;
}
} else {
&parent_dir_fname,
&smb_fname_rel);
if (!ok) {
- saved_errno = errno;
+ status = NT_STATUS_NO_MEMORY;
goto out;
}
}
oldwd_fname = vfs_GetWd(talloc_tos(), conn);
if (oldwd_fname == NULL) {
+ status = map_nt_error_from_unix(errno);
goto out;
}
/* Pin parent directory in place. */
if (vfs_ChDir(conn, parent_dir_fname) == -1) {
+ status = map_nt_error_from_unix(errno);
goto out;
}
/* Ensure the relative path is below the share. */
status = check_reduced_name(conn, parent_dir_fname, smb_fname_rel);
if (!NT_STATUS_IS_OK(status)) {
- saved_errno = map_errno_from_nt_status(status);
goto out;
}
fsp,
flags,
mode);
-
+ if (fd == -1) {
+ status = link_errno_convert(errno);
+ }
fsp_set_fd(fsp, fd);
if (fd != -1 &&
ret = SMB_VFS_FSTAT(fsp, &orig_fsp_name->st);
if (ret != 0) {
+ status = map_nt_error_from_unix(errno);
goto out;
}
fsp->fsp_name->st = orig_fsp_name->st;
fsp_set_fd(fsp, -1);
fd = -1;
- errno = ELOOP;
+ status = NT_STATUS_STOPPED_ON_SYMLINK;
}
}
- if (fd == -1) {
- saved_errno = link_errno_convert(errno);
+ if ((fd == -1) &&
+ (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_NOT_A_DIRECTORY)))
+ {
/*
* Trying to open a symlink to a directory with O_NOFOLLOW and
* O_DIRECTORY can return either of ELOOP and ENOTDIR. So
*
* BUG: https://bugzilla.samba.org/show_bug.cgi?id=12860
*/
- if (saved_errno == ELOOP || saved_errno == ENOTDIR) {
- if (fsp->fsp_name->flags & SMB_FILENAME_POSIX_PATH) {
- /* Never follow symlinks on posix open. */
- goto out;
- }
- if (!lp_follow_symlinks(SNUM(conn))) {
- /* Explicitly no symlinks. */
- goto out;
- }
+ saved_status = status;
- fsp->fsp_name = orig_fsp_name;
+ if (fsp->fsp_name->flags & SMB_FILENAME_POSIX_PATH) {
+ /* Never follow symlinks on posix open. */
+ goto out;
+ }
+ if (!lp_follow_symlinks(SNUM(conn))) {
+ /* Explicitly no symlinks. */
+ goto out;
+ }
- /*
- * We may have a symlink. Follow in userspace
- * to ensure it's under the share definition.
- */
- fd = process_symlink_open(dirfsp,
- fsp,
- smb_fname_rel,
- flags,
- mode,
- link_depth);
- if (fd == -1) {
- if (saved_errno == ENOTDIR &&
- errno == EINVAL) {
- /*
- * O_DIRECTORY on neither a directory,
- * nor a symlink. Just return
- * saved_errno from initial open()
- */
- goto out;
- }
- saved_errno =
- link_errno_convert(errno);
- }
+ fsp->fsp_name = orig_fsp_name;
+
+ /*
+ * We may have a symlink. Follow in userspace
+ * to ensure it's under the share definition.
+ */
+ status = process_symlink_open(dirfsp,
+ fsp,
+ smb_fname_rel,
+ flags,
+ mode,
+ link_depth);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER) &&
+ NT_STATUS_EQUAL(saved_status, NT_STATUS_NOT_A_DIRECTORY))
+ {
+ status = saved_status;
}
}
}
TALLOC_FREE(oldwd_fname);
}
- if (saved_errno != 0) {
- errno = saved_errno;
- }
- return fd;
+ return status;
}
/****************************************************************************
{
struct connection_struct *conn = fsp->conn;
NTSTATUS status = NT_STATUS_OK;
- int fd;
/*
* Never follow symlinks on a POSIX client. The
* Only follow symlinks within a share
* definition.
*/
- fd = non_widelink_open(dirfsp, fsp, smb_fname, flags, mode, 0);
- if (fd == -1) {
- int posix_errno = link_errno_convert(errno);
- status = map_nt_error_from_unix(posix_errno);
- if (errno == EMFILE) {
+ status = non_widelink_open(dirfsp, fsp, smb_fname, flags, mode, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_TOO_MANY_OPENED_FILES)) {
static time_t last_warned = 0L;
if (time((time_t *) NULL) > last_warned) {
DBG_DEBUG("name %s, flags = 0%o mode = 0%o, fd = %d. %s\n",
smb_fname_str_dbg(smb_fname), flags, (int)mode,
- fd, strerror(errno));
+ fsp_get_pathref_fd(fsp), nt_errstr(status));
return status;
}
DBG_DEBUG("name %s, flags = 0%o mode = 0%o, fd = %d\n",
- smb_fname_str_dbg(smb_fname), flags, (int)mode, fd);
+ smb_fname_str_dbg(smb_fname), flags, (int)mode,
+ fsp_get_pathref_fd(fsp));
return status;
}