diff --git a/source3/libsmb/clifile.c b/source3/libsmb/clifile.c index 424354b..37571e5 100644 --- a/source3/libsmb/clifile.c +++ b/source3/libsmb/clifile.c @@ -1795,6 +1795,7 @@ NTSTATUS cli_nt_delete_on_close(struct cli_state *cli, uint16_t fnum, bool flag) struct cli_ntcreate_state { uint16_t vwv[24]; uint16_t fnum; + uint32_t attr; }; static void cli_ntcreate_done(struct tevent_req *subreq); @@ -1880,17 +1881,20 @@ static void cli_ntcreate_done(struct tevent_req *subreq) uint8_t *bytes; NTSTATUS status; - status = cli_smb_recv(subreq, state, NULL, 3, &wct, &vwv, + status = cli_smb_recv(subreq, state, NULL, 26, &wct, &vwv, &num_bytes, &bytes); TALLOC_FREE(subreq); if (tevent_req_nterror(req, status)) { return; } state->fnum = SVAL(vwv+2, 1); + state->attr = IVAL(vwv+21, 1); tevent_req_done(req); } -NTSTATUS cli_ntcreate_recv(struct tevent_req *req, uint16_t *pfnum) +static NTSTATUS cli_ntcreate_recv_ex(struct tevent_req *req, + uint16_t *pfnum, + uint32_t *pfattr) { struct cli_ntcreate_state *state = tevent_req_data( req, struct cli_ntcreate_state); @@ -1900,9 +1904,17 @@ NTSTATUS cli_ntcreate_recv(struct tevent_req *req, uint16_t *pfnum) return status; } *pfnum = state->fnum; + if (pfattr) { + *pfattr = state->attr; + } return NT_STATUS_OK; } +NTSTATUS cli_ntcreate_recv(struct tevent_req *req, uint16_t *pfnum) +{ + return cli_ntcreate_recv_ex(req, pfnum, NULL); +} + NTSTATUS cli_ntcreate(struct cli_state *cli, const char *fname, uint32_t CreatFlags, @@ -1917,6 +1929,7 @@ NTSTATUS cli_ntcreate(struct cli_state *cli, TALLOC_CTX *frame = NULL; struct tevent_context *ev; struct tevent_req *req; + uint32_t attr; NTSTATUS status = NT_STATUS_OK; if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { @@ -1962,7 +1975,25 @@ NTSTATUS cli_ntcreate(struct cli_state *cli, goto fail; } - status = cli_ntcreate_recv(req, pfid); + status = cli_ntcreate_recv_ex(req, pfid, &attr); + + if ((CreateOptions & FILE_NON_DIRECTORY_FILE) && + (attr & FILE_ATTRIBUTE_DIRECTORY)) { + /* + * Some (broken) servers return a valid handle + * for directories even if FILE_NON_DIRECTORY_FILE + * is set. Just close the handle and set the + * error explicitly to NT_STATUS_FILE_IS_A_DIRECTORY. + */ + status = cli_close(cli, *pfid); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + status = NT_STATUS_FILE_IS_A_DIRECTORY; + /* Set this so libsmbclient can retrieve it. */ + cli->raw_status = status; + } + fail: TALLOC_FREE(frame); return status; @@ -1970,6 +2001,7 @@ NTSTATUS cli_ntcreate(struct cli_state *cli, struct cli_nttrans_create_state { uint16_t fnum; + uint32_t attr; }; static void cli_nttrans_create_done(struct tevent_req *subreq); @@ -2083,11 +2115,13 @@ static void cli_nttrans_create_done(struct tevent_req *subreq) return; } state->fnum = SVAL(param, 2); + state->attr = IVAL(param, 48); TALLOC_FREE(param); tevent_req_done(req); } -NTSTATUS cli_nttrans_create_recv(struct tevent_req *req, uint16_t *fnum) +static NTSTATUS cli_nttrans_create_recv_ex(struct tevent_req *req, uint16_t *fnum, + uint32_t *pattr) { struct cli_nttrans_create_state *state = tevent_req_data( req, struct cli_nttrans_create_state); @@ -2097,9 +2131,17 @@ NTSTATUS cli_nttrans_create_recv(struct tevent_req *req, uint16_t *fnum) return status; } *fnum = state->fnum; + if (pattr) { + *pattr = state->attr; + } return NT_STATUS_OK; } +NTSTATUS cli_nttrans_create_recv(struct tevent_req *req, uint16_t *fnum) +{ + return cli_nttrans_create_recv_ex(req, fnum, NULL); +} + NTSTATUS cli_nttrans_create(struct cli_state *cli, const char *fname, uint32_t CreatFlags, @@ -2117,6 +2159,7 @@ NTSTATUS cli_nttrans_create(struct cli_state *cli, TALLOC_CTX *frame = talloc_stackframe(); struct tevent_context *ev; struct tevent_req *req; + uint32_t attr; NTSTATUS status = NT_STATUS_NO_MEMORY; if (smbXcli_conn_has_async_calls(cli->conn)) { @@ -2141,8 +2184,26 @@ NTSTATUS cli_nttrans_create(struct cli_state *cli, if (!tevent_req_poll_ntstatus(req, ev, &status)) { goto fail; } - status = cli_nttrans_create_recv(req, pfid); - fail: + status = cli_nttrans_create_recv_ex(req, pfid, &attr); + + if ((CreateOptions & FILE_NON_DIRECTORY_FILE) && + (attr & FILE_ATTRIBUTE_DIRECTORY)) { + /* + * Some (broken) servers return a valid handle + * for directories even if FILE_NON_DIRECTORY_FILE + * is set. Just close the handle and set the + * error explicitly to NT_STATUS_FILE_IS_A_DIRECTORY. + */ + status = cli_close(cli, *pfid); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + status = NT_STATUS_FILE_IS_A_DIRECTORY; + /* Set this so libsmbclient can retrieve it. */ + cli->raw_status = status; + } + +fail: TALLOC_FREE(frame); return status; }