From 139a8505f727d79b22c4d124b3fbb8c1cebdb16f Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 8 May 2014 11:44:26 -0700 Subject: [PATCH] s3: libsmbclient: Work around bugs in SLES cifsd and Apple smbx SMB1 servers. SLES's cifsd and Apple's smbx do not correctly handle FILE_NON_DIRECTORY_FILE which prevents recursive copies in gvfs from working correctly since GVFS tries to open the directory, expecting ENOTDIR, but it suceeds and appears as a zero byte file. This fix adds code to the synchronous NTCreateX and NTTrans open code that checks if CreateOptions was requested with FILE_NON_DIRECTORY_FILE set, and if the attributes returned include FILE_ATTRIBUTE_DIRECTORY we synchronously close the file handle just opened, and return NT_STATUS_FILE_IS_A_DIRECTORY to the caller. Fixes bug #10587 - Opening directories on SLES's cifsd and Apple's smbx succeeds. https://bugzilla.samba.org/show_bug.cgi?id=10587 Signed-off-by: Jeremy Allison --- source3/libsmb/clifile.c | 72 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 67 insertions(+), 5 deletions(-) diff --git a/source3/libsmb/clifile.c b/source3/libsmb/clifile.c index 424354b..807019b 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, 22, &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_internal(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_internal(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_internal(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,14 @@ 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_internal(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 +2132,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_internal(req, fnum, NULL); +} + NTSTATUS cli_nttrans_create(struct cli_state *cli, const char *fname, uint32_t CreatFlags, @@ -2117,6 +2160,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,7 +2185,25 @@ 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); + status = cli_nttrans_create_recv_internal(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; -- 1.9.1.423.g4596e3a