return NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
- if (S_ISLNK(smb_fname->st.st_ex_mode) &&
- !posix_open)
- {
- /*
- * Don't allow stat opens on symlinks directly unless
- * it's a POSIX open. Match the return code from
- * openat_pathref_fsp().
- */
- return NT_STATUS_OBJECT_NAME_NOT_FOUND;
- }
-
if (!fsp->fsp_flags.is_pathref) {
/*
* There is only one legit case where end up here:
* pathref fsp at this point. The subsequent checks
* assert this.
*/
- if (!(smb_fname->flags & SMB_FILENAME_POSIX_PATH)) {
- DBG_ERR("[%s] is not a POSIX pathname\n",
- smb_fname_str_dbg(smb_fname));
- return NT_STATUS_INTERNAL_ERROR;
- }
if (!S_ISLNK(smb_fname->st.st_ex_mode)) {
DBG_ERR("[%s] is not a symlink\n",
smb_fname_str_dbg(smb_fname));
fsp->file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
fsp->vuid = req ? req->vuid : UID_FIELD_INVALID;
fsp->file_pid = req ? req->smbpid : 0;
- fsp->fsp_flags.can_lock = true;
- fsp->fsp_flags.can_read = ((access_mask & FILE_READ_DATA) != 0);
- fsp->fsp_flags.can_write =
- CAN_WRITE(conn) &&
- ((access_mask & (FILE_WRITE_DATA | FILE_APPEND_DATA)) != 0);
- if (fsp->fsp_name->twrp != 0) {
+ if (file_existed && S_ISLNK(smb_fname->st.st_ex_mode)) {
+ fsp->fsp_flags.can_lock = false;
+ fsp->fsp_flags.can_read = false;
fsp->fsp_flags.can_write = false;
+ } else {
+ fsp->fsp_flags.can_lock = true;
+ fsp->fsp_flags.can_read = ((access_mask & FILE_READ_DATA) !=
+ 0);
+ fsp->fsp_flags.can_write = CAN_WRITE(conn) &&
+ ((access_mask &
+ (FILE_WRITE_DATA |
+ FILE_APPEND_DATA)) != 0) &&
+ (fsp->fsp_name->twrp == 0);
}
fsp->print_file = NULL;
fsp->fsp_flags.modified = false;
if (smbd_smb2_is_compound(smb2req)) {
smb2req->compound_create_err = status;
}
- error = smbd_smb2_create_error(smb2req, status, NULL);
+ error = smbd_smb2_create_error(smb2req,
+ status,
+ symlink_reparse);
if (!NT_STATUS_IS_OK(error)) {
smbd_server_connection_terminate(smb2req->xconn,
nt_errstr(error));
uint64_t out_file_id_persistent;
uint64_t out_file_id_volatile;
struct smb2_create_blobs *out_context_blobs;
+
+ /* symlink error data */
+ struct reparse_data_buffer *symlink_err;
};
static void smbd_smb2_create_purge_replay_cache(struct tevent_req *req,
state->in_create_disposition,
state->in_create_options);
- status = filename_convert_dirfsp(
- req,
- smb1req->conn,
- state->fname,
- ucf_flags,
- state->twrp_time,
- &dirfsp,
- &smb_fname);
+ if (lp_follow_symlinks(SNUM(smb1req->conn)) &&
+ (state->posx == NULL)) {
+ status = filename_convert_dirfsp(mem_ctx,
+ smb1req->conn,
+ state->fname,
+ ucf_flags,
+ state->twrp_time,
+ &dirfsp,
+ &smb_fname);
+ } else {
+ struct smb_filename *smb_fname_rel = NULL;
+
+ status = filename_convert_dirfsp_nosymlink(
+ mem_ctx,
+ smb1req->conn,
+ smb1req->conn->cwd_fsp,
+ state->fname,
+ ucf_flags,
+ state->twrp_time,
+ &dirfsp,
+ &smb_fname,
+ &smb_fname_rel,
+ &state->symlink_err);
+ TALLOC_FREE(smb_fname_rel);
+
+ if ((state->symlink_err != NULL) &&
+ !(state->in_create_options & FILE_OPEN_REPARSE_POINT))
+ {
+ if (dirfsp != NULL) {
+ close_file_free(NULL, &dirfsp, ERROR_CLOSE);
+ }
+ TALLOC_FREE(smb_fname);
+ TALLOC_FREE(smb_fname_rel);
+ status = NT_STATUS_STOPPED_ON_SYMLINK;
+ }
+ }
if (tevent_req_nterror(req, status)) {
return tevent_req_post(req, state->ev);
}
state->out_file_attributes = fdos_mode(state->result);
+ if ((state->out_file_attributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
+ (!(state->in_create_options & FILE_OPEN_REPARSE_POINT)))
+ {
+
+ uint32_t tag;
+ uint8_t *data = NULL;
+ uint32_t len = 0;
+
+ status = fsctl_get_reparse_point(
+ state->result, talloc_tos(), &tag, &data, 65536, &len);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ if (tag != IO_REPARSE_TAG_SYMLINK) {
+ status = NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED;
+ goto fail;
+ }
+
+ state->symlink_err = talloc_zero(state,
+ struct reparse_data_buffer);
+ if (state->symlink_err == NULL) {
+ TALLOC_FREE(data);
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ status = reparse_data_buffer_parse(state->symlink_err,
+ state->symlink_err,
+ data,
+ len);
+ TALLOC_FREE(data);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("reparse_data_buffer_parse failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ /*
+ * Checked above, just to make sure
+ */
+ SMB_ASSERT(state->symlink_err->tag == IO_REPARSE_TAG_SYMLINK);
+
+ DBG_DEBUG("Redirecting to %s\n",
+ state->symlink_err->parsed.lnk.substitute_name);
+
+ status = NT_STATUS_STOPPED_ON_SYMLINK;
+ goto fail;
+ }
+
if (state->mxac != NULL) {
NTTIME last_write_time;
struct smb2_create_blobs *out_context_blobs,
struct reparse_data_buffer **symlink_reparse)
{
- NTSTATUS status;
+ NTSTATUS status = NT_STATUS_OK;
struct smbd_smb2_create_state *state = tevent_req_data(req,
struct smbd_smb2_create_state);
+ bool error;
+
+ error = tevent_req_is_nterror(req, &status);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
+ struct symlink_reparse_struct *lnk = &state->symlink_err
+ ->parsed.lnk;
+ size_t fname_len = strlen(state->fname);
+
+ /*
+ * filename_convert_dirfsp_nosymlink() calculates
+ * unparsed_path_length. Just assert it did not mess up big
+ * time.
+ */
+ SMB_ASSERT(lnk->unparsed_path_length <= fname_len);
+
+ if (lnk->unparsed_path_length != 0) {
+ bool ok;
+ char *unparsed_unix = NULL;
+ char *utf_16 = NULL;
+ size_t utf_16_len;
+
+ unparsed_unix = state->fname + fname_len -
+ lnk->unparsed_path_length;
+
+ ok = convert_string_talloc(talloc_tos(),
+ CH_UNIX,
+ CH_UTF16,
+ unparsed_unix,
+ lnk->unparsed_path_length,
+ &utf_16,
+ &utf_16_len);
+ if (!ok) {
+ tevent_req_received(req);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ TALLOC_FREE(utf_16);
+
+ if (utf_16_len > UINT16_MAX) {
+ tevent_req_received(req);
+ return NT_STATUS_BUFFER_OVERFLOW;
+ }
+ lnk->unparsed_path_length = utf_16_len;
+ }
+ *symlink_reparse = talloc_move(mem_ctx, &state->symlink_err);
+ }
- if (tevent_req_is_nterror(req, &status)) {
+ if (error) {
tevent_req_received(req);
return status;
}