From 62aa7310949ab17e681fd1d04a2f2b2243c5d8da 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 [1] 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 cli_open() 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 | 83 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 74 insertions(+), 9 deletions(-) diff --git a/source3/libsmb/clifile.c b/source3/libsmb/clifile.c index 424354b..64f2216 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,10 +1904,18 @@ 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(struct cli_state *cli, +NTSTATUS cli_ntcreate_recv(struct tevent_req *req, uint16_t *pfnum) +{ + return cli_ntcreate_recv_internal(req, pfnum, NULL); +} + +static NTSTATUS cli_ntcreate_internal(struct cli_state *cli, const char *fname, uint32_t CreatFlags, uint32_t DesiredAccess, @@ -1912,7 +1924,8 @@ NTSTATUS cli_ntcreate(struct cli_state *cli, uint32_t CreateDisposition, uint32_t CreateOptions, uint8_t SecurityFlags, - uint16_t *pfid) + uint16_t *pfid, + uint32_t *pattr) { TALLOC_CTX *frame = NULL; struct tevent_context *ev; @@ -1920,7 +1933,8 @@ NTSTATUS cli_ntcreate(struct cli_state *cli, NTSTATUS status = NT_STATUS_OK; if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { - return cli_smb2_create_fnum(cli, + struct smb2_create_returns cr; + status = cli_smb2_create_fnum(cli, fname, CreatFlags, DesiredAccess, @@ -1929,7 +1943,13 @@ NTSTATUS cli_ntcreate(struct cli_state *cli, CreateDisposition, CreateOptions, pfid, - NULL); + &cr); + if (NT_STATUS_IS_OK(status)) { + if (pattr) { + *pattr = cr.file_attributes; + } + } + return status; } frame = talloc_stackframe(); @@ -1962,12 +1982,37 @@ NTSTATUS cli_ntcreate(struct cli_state *cli, goto fail; } - status = cli_ntcreate_recv(req, pfid); + status = cli_ntcreate_recv_internal(req, pfid, pattr); + fail: TALLOC_FREE(frame); return status; } +NTSTATUS cli_ntcreate(struct cli_state *cli, + const char *fname, + uint32_t CreatFlags, + uint32_t DesiredAccess, + uint32_t FileAttributes, + uint32_t ShareAccess, + uint32_t CreateDisposition, + uint32_t CreateOptions, + uint8_t SecurityFlags, + uint16_t *pfid) +{ + return cli_ntcreate_internal(cli, + fname, + CreatFlags, + DesiredAccess, + FileAttributes, + ShareAccess, + CreateDisposition, + CreateOptions, + SecurityFlags, + pfid, + NULL); +} + struct cli_nttrans_create_state { uint16_t fnum; }; @@ -2353,6 +2398,7 @@ NTSTATUS cli_open(struct cli_state *cli, const char *fname, int flags, unsigned int openfn = 0; unsigned int dos_deny = 0; uint32_t access_mask, share_mode, create_disposition, create_options; + uint32_t attr; /* Do the initial mapping into OpenX parameters. */ if (flags & O_CREAT) { @@ -2424,7 +2470,7 @@ NTSTATUS cli_open(struct cli_state *cli, const char *fname, int flags, goto try_openx; } - status = cli_ntcreate(cli, + status = cli_ntcreate_internal(cli, fname, 0, access_mask, @@ -2433,7 +2479,8 @@ NTSTATUS cli_open(struct cli_state *cli, const char *fname, int flags, create_disposition, create_options, 0, - pfnum); + pfnum, + &attr); /* Try and cope will all varients of "we don't do this call" and fall back to openX. */ @@ -2450,6 +2497,24 @@ NTSTATUS cli_open(struct cli_state *cli, const char *fname, int flags, goto try_openx; } + if (NT_STATUS_IS_OK(status) && + (create_options & 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, *pfnum); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + status = NT_STATUS_FILE_IS_A_DIRECTORY; + /* Set this so libsmbclient can retrieve it. */ + cli->raw_status = status; + } + return status; try_openx: -- 1.9.1.423.g4596e3a