From f55c7614bd7360b484f15c2290ab88195bb78094 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 27 May 2009 17:28:23 -0700 Subject: Add aync POSIX hardlink and symlink and torture test for them. Missing call cli_readlink() is next. Jeremy. --- source3/client/client.c | 4 +- source3/include/proto.h | 20 +- source3/libsmb/clifile.c | 451 ++++++++++++++++++++++++++++++---------------- source3/torture/torture.c | 73 +++++++- 4 files changed, 391 insertions(+), 157 deletions(-) diff --git a/source3/client/client.c b/source3/client/client.c index 13dee6081d..d7c554efc0 100644 --- a/source3/client/client.c +++ b/source3/client/client.c @@ -2725,7 +2725,7 @@ static int cmd_link(void) return 1; } - if (!cli_unix_hardlink(targetcli, targetname, newname)) { + if (!NT_STATUS_IS_OK(cli_posix_hardlink(targetcli, targetname, newname))) { d_printf("%s linking files (%s -> %s)\n", cli_errstr(targetcli), newname, oldname); return 1; } @@ -2776,7 +2776,7 @@ static int cmd_symlink(void) return 1; } - if (!cli_unix_symlink(targetcli, targetname, newname)) { + if (!NT_STATUS_IS_OK(cli_posix_symlink(targetcli, targetname, newname))) { d_printf("%s symlinking files (%s -> %s)\n", cli_errstr(targetcli), newname, targetname); return 1; diff --git a/source3/include/proto.h b/source3/include/proto.h index 717a972505..a918c29aaa 100644 --- a/source3/include/proto.h +++ b/source3/include/proto.h @@ -2332,12 +2332,28 @@ void cli_reset_error(struct cli_state *cli); /* The following definitions come from libsmb/clifile.c */ +struct tevent_req *cli_posix_symlink_send(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct cli_state *cli, + const char *oldname, + const char *newname); +NTSTATUS cli_posix_symlink_recv(struct tevent_req *req); +NTSTATUS cli_posix_symlink(struct cli_state *cli, + const char *oldname, + const char *newname); +struct tevent_req *cli_posix_hardlink_send(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct cli_state *cli, + const char *oldname, + const char *newname); +NTSTATUS cli_posix_hardlink_recv(struct tevent_req *req); +NTSTATUS cli_posix_hardlink(struct cli_state *cli, + const char *oldname, + const char *newname); uint32_t unix_perms_to_wire(mode_t perms); mode_t wire_perms_to_unix(uint32_t perms); bool cli_unix_getfacl(struct cli_state *cli, const char *name, size_t *prb_size, char **retbuf); bool cli_unix_stat(struct cli_state *cli, const char *name, SMB_STRUCT_STAT *sbuf); -bool cli_unix_symlink(struct cli_state *cli, const char *oldname, const char *newname); -bool cli_unix_hardlink(struct cli_state *cli, const char *oldname, const char *newname); bool cli_unix_chmod(struct cli_state *cli, const char *fname, mode_t mode); bool cli_unix_chown(struct cli_state *cli, const char *fname, uid_t uid, gid_t gid); struct tevent_req *cli_rename_send(TALLOC_CTX *mem_ctx, diff --git a/source3/libsmb/clifile.c b/source3/libsmb/clifile.c index 1225aa6bae..e48656b698 100644 --- a/source3/libsmb/clifile.c +++ b/source3/libsmb/clifile.c @@ -20,74 +20,328 @@ #include "includes.h" +/*********************************************************** + Common function for pushing stings, used by smb_bytes_push_str() + and trans_bytes_push_str(). Only difference is the align_odd + parameter setting. +***********************************************************/ + +static uint8_t *internal_bytes_push_str(uint8_t *buf, bool ucs2, + const char *str, size_t str_len, + bool align_odd, + size_t *pconverted_size) +{ + size_t buflen; + char *converted; + size_t converted_size; + + if (buf == NULL) { + return NULL; + } + + buflen = talloc_get_size(buf); + + if (align_odd && ucs2 && (buflen % 2 == 0)) { + /* + * We're pushing into an SMB buffer, align odd + */ + buf = TALLOC_REALLOC_ARRAY(NULL, buf, uint8_t, buflen + 1); + if (buf == NULL) { + return NULL; + } + buf[buflen] = '\0'; + buflen += 1; + } + + if (!convert_string_talloc(talloc_tos(), CH_UNIX, + ucs2 ? CH_UTF16LE : CH_DOS, + str, str_len, &converted, + &converted_size, true)) { + return NULL; + } + + buf = TALLOC_REALLOC_ARRAY(NULL, buf, uint8_t, + buflen + converted_size); + if (buf == NULL) { + TALLOC_FREE(converted); + return NULL; + } + + memcpy(buf + buflen, converted, converted_size); + + TALLOC_FREE(converted); + + if (pconverted_size) { + *pconverted_size = converted_size; + } + + return buf; +} + +/*********************************************************** + Push a string into an SMB buffer, with odd byte alignment + if it's a UCS2 string. +***********************************************************/ + +uint8_t *smb_bytes_push_str(uint8_t *buf, bool ucs2, + const char *str, size_t str_len, + size_t *pconverted_size) +{ + return internal_bytes_push_str(buf, ucs2, str, str_len, + true, pconverted_size); +} + +/*********************************************************** + Same as smb_bytes_push_str(), but without the odd byte + align for ucs2 (we're pushing into a param or data block). + static for now, although this will probably change when + other modules use async trans calls. +***********************************************************/ + +static uint8_t *trans2_bytes_push_str(uint8_t *buf, bool ucs2, + const char *str, size_t str_len, + size_t *pconverted_size) +{ + return internal_bytes_push_str(buf, ucs2, str, str_len, + false, pconverted_size); +} + + /**************************************************************************** Hard/Symlink a file (UNIX extensions). Creates new name (sym)linked to oldname. ****************************************************************************/ -static bool cli_link_internal(struct cli_state *cli, const char *oldname, const char *newname, bool hard_link) +struct link_state { + uint16_t setup; + uint8_t *param; + uint8_t *data; +}; + +static void cli_posix_link_internal_done(struct tevent_req *subreq) { - unsigned int data_len = 0; - unsigned int param_len = 0; - uint16_t setup = TRANSACT2_SETPATHINFO; - char *param; - char *data; - char *rparam=NULL, *rdata=NULL; - char *p; - size_t oldlen = 2*(strlen(oldname)+1); - size_t newlen = 2*(strlen(newname)+1); + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct link_state *state = tevent_req_data(req, struct link_state); + NTSTATUS status; + + status = cli_trans_recv(subreq, state, NULL, NULL, NULL, NULL, NULL, NULL); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; + } + tevent_req_done(req); +} - param = SMB_MALLOC_ARRAY(char, 6+newlen+2); +static struct tevent_req *cli_posix_link_internal_send(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct cli_state *cli, + const char *oldname, + const char *newname, + bool hardlink) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct link_state *state = NULL; - if (!param) { - return false; + req = tevent_req_create(mem_ctx, &state, struct link_state); + if (req == NULL) { + return NULL; } - data = SMB_MALLOC_ARRAY(char, oldlen+2); + /* Setup setup word. */ + SSVAL(&state->setup, 0, TRANSACT2_SETPATHINFO); - if (!data) { - SAFE_FREE(param); - return false; + /* Setup param array. */ + state->param = talloc_array(state, uint8_t, 6); + if (tevent_req_nomem(state->param, req)) { + return tevent_req_post(req, ev); } + memset(state->param, '\0', 6); + SSVAL(state->param,0,hardlink ? SMB_SET_FILE_UNIX_HLINK : SMB_SET_FILE_UNIX_LINK); - SSVAL(param,0,hard_link ? SMB_SET_FILE_UNIX_HLINK : SMB_SET_FILE_UNIX_LINK); - SIVAL(param,2,0); - p = ¶m[6]; + state->param = trans2_bytes_push_str(state->param, cli_ucs2(cli), newname, + strlen(newname)+1, NULL); - p += clistr_push(cli, p, newname, newlen, STR_TERMINATE); - param_len = PTR_DIFF(p, param); + if (tevent_req_nomem(state->param, req)) { + return tevent_req_post(req, ev); + } - p = data; - p += clistr_push(cli, p, oldname, oldlen, STR_TERMINATE); - data_len = PTR_DIFF(p, data); + /* Setup data array. */ + state->data = talloc_array(state, uint8_t, 0); + if (tevent_req_nomem(state->data, req)) { + return tevent_req_post(req, ev); + } + state->data = trans2_bytes_push_str(state->data, cli_ucs2(cli), oldname, + strlen(oldname)+1, NULL); - if (!cli_send_trans(cli, SMBtrans2, - NULL, /* name */ - -1, 0, /* fid, flags */ - &setup, 1, 0, /* setup, length, max */ - param, param_len, 2, /* param, length, max */ - data, data_len, cli->max_xmit /* data, length, max */ - )) { - SAFE_FREE(data); - SAFE_FREE(param); - return false; + subreq = cli_trans_send(state, /* mem ctx. */ + ev, /* event ctx. */ + cli, /* cli_state. */ + SMBtrans2, /* cmd. */ + NULL, /* pipe name. */ + -1, /* fid. */ + 0, /* function. */ + 0, /* flags. */ + &state->setup, /* setup. */ + 1, /* num setup uint16_t words. */ + 0, /* max returned setup. */ + state->param, /* param. */ + talloc_get_size(state->param), /* num param. */ + 2, /* max returned param. */ + state->data, /* data. */ + talloc_get_size(state->data), /* num data. */ + 0); /* max returned data. */ + + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); } + tevent_req_set_callback(subreq, cli_posix_link_internal_done, req); + return req; +} - SAFE_FREE(data); - SAFE_FREE(param); +/**************************************************************************** + Symlink a file (UNIX extensions). +****************************************************************************/ - if (!cli_receive_trans(cli, SMBtrans2, - &rparam, ¶m_len, - &rdata, &data_len)) { - return false; +struct tevent_req *cli_posix_symlink_send(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct cli_state *cli, + const char *oldname, + const char *newname) +{ + return cli_posix_link_internal_send(mem_ctx, ev, cli, + oldname, newname, false); +} + +NTSTATUS cli_posix_symlink_recv(struct tevent_req *req) +{ + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; } + return NT_STATUS_OK; +} - SAFE_FREE(data); - SAFE_FREE(param); - SAFE_FREE(rdata); - SAFE_FREE(rparam); +NTSTATUS cli_posix_symlink(struct cli_state *cli, + const char *oldname, + const char *newname) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct event_context *ev = NULL; + struct tevent_req *req = NULL; + NTSTATUS status = NT_STATUS_OK; - return true; + if (cli_has_async_calls(cli)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = event_context_init(frame); + if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + req = cli_posix_symlink_send(frame, + ev, + cli, + oldname, + newname); + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + if (!tevent_req_poll(req, ev)) { + status = map_nt_error_from_unix(errno); + goto fail; + } + + status = cli_posix_symlink_recv(req); + + fail: + TALLOC_FREE(frame); + if (!NT_STATUS_IS_OK(status)) { + cli_set_error(cli, status); + } + return status; +} + +/**************************************************************************** + Hard link a file (UNIX extensions). +****************************************************************************/ + +struct tevent_req *cli_posix_hardlink_send(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct cli_state *cli, + const char *oldname, + const char *newname) +{ + return cli_posix_link_internal_send(mem_ctx, ev, cli, + oldname, newname, true); +} + +NTSTATUS cli_posix_hardlink_recv(struct tevent_req *req) +{ + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + return NT_STATUS_OK; +} + +NTSTATUS cli_posix_hardlink(struct cli_state *cli, + const char *oldname, + const char *newname) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct event_context *ev = NULL; + struct tevent_req *req = NULL; + NTSTATUS status = NT_STATUS_OK; + + if (cli_has_async_calls(cli)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = event_context_init(frame); + if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + req = cli_posix_hardlink_send(frame, + ev, + cli, + oldname, + newname); + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + if (!tevent_req_poll(req, ev)) { + status = map_nt_error_from_unix(errno); + goto fail; + } + + status = cli_posix_hardlink_recv(req); + + fail: + TALLOC_FREE(frame); + if (!NT_STATUS_IS_OK(status)) { + cli_set_error(cli, status); + } + return status; } /**************************************************************************** @@ -325,25 +579,6 @@ bool cli_unix_stat(struct cli_state *cli, const char *name, SMB_STRUCT_STAT *sbu return true; } - -/**************************************************************************** - Symlink a file (UNIX extensions). -****************************************************************************/ - -bool cli_unix_symlink(struct cli_state *cli, const char *oldname, const char *newname) -{ - return cli_link_internal(cli, oldname, newname, False); -} - -/**************************************************************************** - Hard a file (UNIX extensions). -****************************************************************************/ - -bool cli_unix_hardlink(struct cli_state *cli, const char *oldname, const char *newname) -{ - return cli_link_internal(cli, oldname, newname, True); -} - /**************************************************************************** Chmod or chown a file internal (UNIX extensions). ****************************************************************************/ @@ -1288,92 +1523,6 @@ NTSTATUS cli_ntcreate(struct cli_state *cli, return status; } -/*********************************************************** - Common function for pushing stings, used by smb_bytes_push_str() - and trans_bytes_push_str(). Only difference is the align_odd - parameter setting. -***********************************************************/ - -static uint8_t *internal_bytes_push_str(uint8_t *buf, bool ucs2, - const char *str, size_t str_len, - bool align_odd, - size_t *pconverted_size) -{ - size_t buflen; - char *converted; - size_t converted_size; - - if (buf == NULL) { - return NULL; - } - - buflen = talloc_get_size(buf); - - if (align_odd && ucs2 && (buflen % 2 == 0)) { - /* - * We're pushing into an SMB buffer, align odd - */ - buf = TALLOC_REALLOC_ARRAY(NULL, buf, uint8_t, buflen + 1); - if (buf == NULL) { - return NULL; - } - buf[buflen] = '\0'; - buflen += 1; - } - - if (!convert_string_talloc(talloc_tos(), CH_UNIX, - ucs2 ? CH_UTF16LE : CH_DOS, - str, str_len, &converted, - &converted_size, true)) { - return NULL; - } - - buf = TALLOC_REALLOC_ARRAY(NULL, buf, uint8_t, - buflen + converted_size); - if (buf == NULL) { - TALLOC_FREE(converted); - return NULL; - } - - memcpy(buf + buflen, converted, converted_size); - - TALLOC_FREE(converted); - - if (pconverted_size) { - *pconverted_size = converted_size; - } - - return buf; -} - -/*********************************************************** - Push a string into an SMB buffer, with odd byte alignment - if it's a UCS2 string. -***********************************************************/ - -uint8_t *smb_bytes_push_str(uint8_t *buf, bool ucs2, - const char *str, size_t str_len, - size_t *pconverted_size) -{ - return internal_bytes_push_str(buf, ucs2, str, str_len, - true, pconverted_size); -} - -/*********************************************************** - Same as smb_bytes_push_str(), but without the odd byte - align for ucs2 (we're pushing into a param or data block). - static for now, although this will probably change when - other modules use async trans calls. -***********************************************************/ - -static uint8_t *trans2_bytes_push_str(uint8_t *buf, bool ucs2, - const char *str, size_t str_len, - size_t *pconverted_size) -{ - return internal_bytes_push_str(buf, ucs2, str, str_len, - false, pconverted_size); -} - /**************************************************************************** Open a file WARNING: if you open with O_WRONLY then getattrE won't work! diff --git a/source3/torture/torture.c b/source3/torture/torture.c index 41343cab87..bff8d07f1f 100644 --- a/source3/torture/torture.c +++ b/source3/torture/torture.c @@ -4135,8 +4135,11 @@ static bool run_opentest(int dummy) static bool run_simple_posix_open_test(int dummy) { static struct cli_state *cli1; - const char *fname = "\\posix:file"; - const char *dname = "\\posix:dir"; + const char *fname = "posix:file"; + const char *hname = "posix:hlink"; + const char *sname = "posix:symlink"; + const char *dname = "posix:dir"; + char buf[10]; uint16 major, minor; uint32 caplow, caphigh; uint16_t fnum1 = (uint16_t)-1; @@ -4171,6 +4174,10 @@ static bool run_simple_posix_open_test(int dummy) cli_posix_unlink(cli1, fname); cli_setatr(cli1, dname, 0, 0); cli_posix_rmdir(cli1, dname); + cli_setatr(cli1, hname, 0, 0); + cli_posix_unlink(cli1, hname); + cli_setatr(cli1, sname, 0, 0); + cli_posix_unlink(cli1, sname); /* Create a directory. */ if (!NT_STATUS_IS_OK(cli_posix_mkdir(cli1, dname, 0777))) { @@ -4222,6 +4229,64 @@ static bool run_simple_posix_open_test(int dummy) } } + /* Create the file. */ + if (!NT_STATUS_IS_OK(cli_posix_open(cli1, fname, O_RDWR|O_CREAT|O_EXCL, 0600, &fnum1))) { + printf("POSIX create of %s failed (%s)\n", fname, cli_errstr(cli1)); + goto out; + } + + /* Write some data into it. */ + if (cli_write(cli1, fnum1, 0, "TEST DATA\n", 0, 10) != 10) { + printf("cli_write failed: %s\n", cli_errstr(cli1)); + goto out; + } + + cli_close(cli1, fnum1); + + /* Now create a hardlink. */ + if (!NT_STATUS_IS_OK(cli_posix_hardlink(cli1, fname, hname))) { + printf("POSIX hardlink of %s failed (%s)\n", hname, cli_errstr(cli1)); + goto out; + } + + /* Now create a symlink. */ + if (!NT_STATUS_IS_OK(cli_posix_symlink(cli1, fname, sname))) { + printf("POSIX symlink of %s failed (%s)\n", sname, cli_errstr(cli1)); + goto out; + } + + /* Open the hardlink for read. */ + if (!NT_STATUS_IS_OK(cli_posix_open(cli1, hname, O_RDONLY, 0, &fnum1))) { + printf("POSIX open of %s failed (%s)\n", hname, cli_errstr(cli1)); + goto out; + } + + if (cli_read(cli1, fnum1, buf, 0, 10) != 10) { + printf("POSIX read of %s failed (%s)\n", hname, cli_errstr(cli1)); + goto out; + } + + if (memcmp(buf, "TEST DATA\n", 10)) { + printf("invalid data read from hardlink\n"); + goto out; + } + + cli_close(cli1, fnum1); + + /* Open the symlink for read - this should fail. A POSIX + client should not be doing opens on a symlink. */ + if (NT_STATUS_IS_OK(cli_posix_open(cli1, sname, O_RDONLY, 0, &fnum1))) { + printf("POSIX open of %s succeeded (should have failed)\n", sname); + goto out; + } else { + if (!check_error(__LINE__, cli1, ERRDOS, ERRbadpath, + NT_STATUS_OBJECT_PATH_NOT_FOUND)) { + goto out; + } + } + + /* TODO. Add and test cli_posix_readlink() call. */ + if (!NT_STATUS_IS_OK(cli_posix_rmdir(cli1, dname))) { printf("POSIX rmdir failed (%s)\n", cli_errstr(cli1)); goto out; @@ -4237,6 +4302,10 @@ static bool run_simple_posix_open_test(int dummy) fnum1 = (uint16_t)-1; } + cli_setatr(cli1, sname, 0, 0); + cli_posix_unlink(cli1, sname); + cli_setatr(cli1, hname, 0, 0); + cli_posix_unlink(cli1, hname); cli_setatr(cli1, fname, 0, 0); cli_posix_unlink(cli1, fname); cli_setatr(cli1, dname, 0, 0); -- cgit