summaryrefslogtreecommitdiffstats
path: root/source3/torture/test_posix.c
diff options
context:
space:
mode:
Diffstat (limited to 'source3/torture/test_posix.c')
-rw-r--r--source3/torture/test_posix.c1972
1 files changed, 1972 insertions, 0 deletions
diff --git a/source3/torture/test_posix.c b/source3/torture/test_posix.c
new file mode 100644
index 0000000..ca40336
--- /dev/null
+++ b/source3/torture/test_posix.c
@@ -0,0 +1,1972 @@
+/*
+ Unix SMB/CIFS implementation.
+ Copyright (C) Ralph Boehme 2020
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/proto.h"
+#include "libcli/security/security.h"
+#include "libsmb/libsmb.h"
+#include "libsmb/clirap.h"
+#include "libsmb/proto.h"
+#include "../libcli/smb/smbXcli_base.h"
+#include "util_sd.h"
+#include "trans2.h"
+
+extern struct cli_credentials *torture_creds;
+extern fstring host, workgroup, share, password, username, myname;
+
+struct posix_test_entry {
+ const char *name;
+ const char *target;
+ const char *expected;
+ uint32_t attr_win;
+ uint32_t attr_lin;
+ uint64_t returned_size;
+ bool ok;
+};
+
+enum client_flavour { WINDOWS, POSIX };
+
+struct posix_test_state {
+ enum client_flavour flavour;
+ struct posix_test_entry *entries;
+};
+
+static NTSTATUS posix_ls_fn(struct file_info *finfo,
+ const char *name,
+ void *_state)
+{
+ struct posix_test_state *state =
+ (struct posix_test_state *)_state;
+ struct posix_test_entry *e = state->entries;
+
+ for (; e->name != NULL; e++) {
+ uint32_t attr;
+ if (!strequal(finfo->name, e->expected)) {
+ continue;
+ }
+ if (state->flavour == WINDOWS) {
+ attr = e->attr_win;
+ } else {
+ attr = e->attr_lin;
+ }
+ if (attr != finfo->attr) {
+ break;
+ }
+ e->ok = true;
+ e->returned_size = finfo->size;
+ break;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static void posix_test_entries_reset(struct posix_test_state *state)
+{
+ struct posix_test_entry *e = state->entries;
+
+ for (; e->name != NULL; e++) {
+ e->ok = false;
+ e->returned_size = 0;
+ }
+}
+
+static bool posix_test_entry_check(struct posix_test_state *state,
+ const char *name,
+ bool expected,
+ uint64_t expected_size)
+{
+ struct posix_test_entry *e = state->entries;
+ bool result = false;
+
+ for (; e->name != NULL; e++) {
+ if (strequal(name, e->name)) {
+ result = e->ok;
+ break;
+ }
+ }
+ if (e->name == NULL) {
+ printf("test failed, unknown name: %s\n", name);
+ return false;
+ }
+
+ if (expected == result) {
+ return true;
+ }
+
+ printf("test failed, %s: %s\n",
+ expected ? "missing" : "unexpected",
+ name);
+
+ return false;
+}
+
+/*
+ Test non-POSIX vs POSIX ls * of symlinks
+ */
+bool run_posix_ls_wildcard_test(int dummy)
+{
+ TALLOC_CTX *frame = NULL;
+ struct cli_state *cli_unix = NULL;
+ struct cli_state *cli_win = NULL;
+ uint16_t fnum = (uint16_t)-1;
+ NTSTATUS status;
+ const char *file = "file";
+ const char *symlnk_dangling = "dangling";
+ const char *symlnk_dst_dangling = "xxxxxxx";
+ const char *symlnk_in_share = "symlnk_in_share";
+ const char *symlnk_dst_in_share = file;
+ const char *symlnk_outside_share = "symlnk_outside_share";
+ const char *symlnk_dst_outside_share = "/etc/passwd";
+ struct posix_test_entry entries[] = {
+ {
+ .name = file,
+ .target = NULL,
+ .expected = file,
+ .attr_win = FILE_ATTRIBUTE_ARCHIVE,
+ .attr_lin = FILE_ATTRIBUTE_ARCHIVE,
+ }, {
+ .name = symlnk_dangling,
+ .target = symlnk_dst_dangling,
+ .expected = symlnk_dangling,
+ .attr_win = FILE_ATTRIBUTE_INVALID,
+ .attr_lin = FILE_ATTRIBUTE_NORMAL,
+ }, {
+ .name = symlnk_in_share,
+ .target = symlnk_dst_in_share,
+ .expected = symlnk_in_share,
+ .attr_win = FILE_ATTRIBUTE_ARCHIVE,
+ .attr_lin = FILE_ATTRIBUTE_NORMAL,
+ }, {
+ .name = symlnk_outside_share,
+ .target = symlnk_dst_outside_share,
+ .expected = symlnk_outside_share,
+ .attr_win = FILE_ATTRIBUTE_INVALID,
+ .attr_lin = FILE_ATTRIBUTE_NORMAL,
+ }, {
+ .name = NULL,
+ }
+ };
+ struct posix_test_state _state = {
+ .entries = entries,
+ };
+ struct posix_test_state *state = &_state;
+ int i;
+ bool correct = false;
+
+ frame = talloc_stackframe();
+
+ printf("Starting POSIX-LS-WILDCARD test\n");
+
+ if (!torture_open_connection(&cli_unix, 0)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ if (!torture_open_connection(&cli_win, 0)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ torture_conn_set_sockopt(cli_unix);
+ torture_conn_set_sockopt(cli_win);
+
+ status = torture_setup_unix_extensions(cli_unix);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ cli_posix_unlink(cli_unix, file);
+ cli_posix_unlink(cli_unix, symlnk_dangling);
+ cli_posix_unlink(cli_unix, symlnk_in_share);
+ cli_posix_unlink(cli_unix, symlnk_outside_share);
+
+ status = cli_posix_open(cli_unix,
+ file,
+ O_RDWR|O_CREAT,
+ 0666,
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_open of %s failed error %s\n",
+ file,
+ nt_errstr(status));
+ goto out;
+ }
+
+ status = cli_close(cli_unix, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_close failed %s\n", nt_errstr(status));
+ goto out;
+ }
+ fnum = (uint16_t)-1;
+
+ for (i = 0; entries[i].name != NULL; i++) {
+ if (entries[i].target == NULL) {
+ continue;
+ }
+ status = cli_posix_symlink(cli_unix,
+ entries[i].target,
+ entries[i].name);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("POSIX symlink of %s failed (%s)\n",
+ symlnk_dangling, nt_errstr(status));
+ goto out;
+ }
+ }
+
+ printf("Doing Windows ls *\n");
+ state->flavour = WINDOWS;
+
+ status = cli_list(cli_win, "*", 0, posix_ls_fn, state);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_list failed %s\n", nt_errstr(status));
+ goto out;
+ }
+
+ if (!posix_test_entry_check(state, file, true, 0)) {
+ goto out;
+ }
+ if (!posix_test_entry_check(state, symlnk_dangling, false, 0)) {
+ goto out;
+ }
+ if (!posix_test_entry_check(state, symlnk_outside_share, false, 0)) {
+ goto out;
+ }
+ if (!posix_test_entry_check(state, symlnk_in_share, true, 0)) {
+ goto out;
+ }
+
+ posix_test_entries_reset(state);
+
+ printf("Doing POSIX ls *\n");
+ state->flavour = POSIX;
+
+ status = cli_list(cli_unix, "*", 0, posix_ls_fn, state);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_close failed %s\n", nt_errstr(status));
+ goto out;
+ }
+
+ if (!posix_test_entry_check(state, file, true, 0)) {
+ goto out;
+ }
+ if (!posix_test_entry_check(state,
+ symlnk_dangling,
+ true,
+ strlen(symlnk_dst_dangling)))
+ {
+ goto out;
+ }
+ if (!posix_test_entry_check(state,
+ symlnk_outside_share,
+ true,
+ strlen(symlnk_dst_outside_share)))
+ {
+ goto out;
+ }
+ if (!posix_test_entry_check(state,
+ symlnk_in_share,
+ true,
+ strlen(symlnk_dst_in_share))) {
+ goto out;
+ }
+
+ printf("POSIX-LS-WILDCARD test passed\n");
+ correct = true;
+
+out:
+ cli_posix_unlink(cli_unix, file);
+ cli_posix_unlink(cli_unix, symlnk_dangling);
+ cli_posix_unlink(cli_unix, symlnk_in_share);
+ cli_posix_unlink(cli_unix, symlnk_outside_share);
+
+ if (!torture_close_connection(cli_unix)) {
+ correct = false;
+ }
+ if (!torture_close_connection(cli_win)) {
+ correct = false;
+ }
+
+ TALLOC_FREE(frame);
+ return correct;
+}
+
+/*
+ Test non-POSIX vs POSIX ls single of symlinks
+ */
+bool run_posix_ls_single_test(int dummy)
+{
+ TALLOC_CTX *frame = NULL;
+ struct cli_state *cli_unix = NULL;
+ struct cli_state *cli_win = NULL;
+ uint16_t fnum = (uint16_t)-1;
+ NTSTATUS status;
+ const char *file = "file";
+ const char *symlnk_dangling = "dangling";
+ const char *symlnk_dst_dangling = "xxxxxxx";
+ const char *symlnk_in_share = "symlnk_in_share";
+ const char *symlnk_dst_in_share = file;
+ const char *symlnk_outside_share = "symlnk_outside_share";
+ const char *symlnk_dst_outside_share = "/etc/passwd";
+ struct posix_test_entry entries[] = {
+ {
+ .name = file,
+ .target = NULL,
+ .expected = file,
+ .attr_win = FILE_ATTRIBUTE_ARCHIVE,
+ .attr_lin = FILE_ATTRIBUTE_ARCHIVE,
+ }, {
+ .name = symlnk_dangling,
+ .target = symlnk_dst_dangling,
+ .expected = symlnk_dangling,
+ .attr_win = FILE_ATTRIBUTE_INVALID,
+ .attr_lin = FILE_ATTRIBUTE_NORMAL,
+ }, {
+ .name = symlnk_in_share,
+ .target = symlnk_dst_in_share,
+ .expected = symlnk_in_share,
+ .attr_win = FILE_ATTRIBUTE_ARCHIVE,
+ .attr_lin = FILE_ATTRIBUTE_NORMAL,
+ }, {
+ .name = symlnk_outside_share,
+ .target = symlnk_dst_outside_share,
+ .expected = symlnk_outside_share,
+ .attr_win = FILE_ATTRIBUTE_INVALID,
+ .attr_lin = FILE_ATTRIBUTE_NORMAL,
+ }, {
+ .name = NULL,
+ }
+ };
+ struct posix_test_state _state = {
+ .entries = &entries[0],
+ };
+ struct posix_test_state *state = &_state;
+ int i;
+ bool correct = false;
+
+ frame = talloc_stackframe();
+
+ printf("Starting POSIX-LS-SINGLE test\n");
+
+ if (!torture_open_connection(&cli_unix, 0)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ if (!torture_init_connection(&cli_win)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ status = smbXcli_negprot(cli_win->conn,
+ cli_win->timeout,
+ lp_client_min_protocol(),
+ lp_client_max_protocol(),
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smbXcli_negprot returned %s\n", nt_errstr(status));
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ status = cli_session_setup_creds(cli_win, torture_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_sesssetup returned %s\n", nt_errstr(status));
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ status = cli_tree_connect(cli_win, share, "?????", NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_tree_connect returned %s\n", nt_errstr(status));
+ TALLOC_FREE(frame);
+ return false;
+ }
+ torture_conn_set_sockopt(cli_unix);
+ torture_conn_set_sockopt(cli_win);
+
+ status = torture_setup_unix_extensions(cli_unix);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ cli_posix_unlink(cli_unix, file);
+ cli_posix_unlink(cli_unix, symlnk_dangling);
+ cli_posix_unlink(cli_unix, symlnk_in_share);
+ cli_posix_unlink(cli_unix, symlnk_outside_share);
+
+ status = cli_posix_open(cli_unix,
+ file,
+ O_RDWR|O_CREAT,
+ 0666,
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_open of %s failed error %s\n",
+ file,
+ nt_errstr(status));
+ goto out;
+ }
+
+ status = cli_close(cli_unix, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_close failed %s\n", nt_errstr(status));
+ goto out;
+ }
+ fnum = (uint16_t)-1;
+
+ for (i = 0; entries[i].name != NULL; i++) {
+ if (entries[i].target == NULL) {
+ continue;
+ }
+ status = cli_posix_symlink(cli_unix,
+ entries[i].target,
+ entries[i].name);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("POSIX symlink of %s failed (%s)\n",
+ symlnk_dangling, nt_errstr(status));
+ goto out;
+ }
+ }
+
+ printf("Doing Windows ls single\n");
+ state->flavour = WINDOWS;
+
+ cli_list(cli_win, file, 0, posix_ls_fn, state);
+ cli_list(cli_win, symlnk_dangling, 0, posix_ls_fn, state);
+ cli_list(cli_win, symlnk_outside_share, 0, posix_ls_fn, state);
+ cli_list(cli_win, symlnk_in_share, 0, posix_ls_fn, state);
+
+ if (!posix_test_entry_check(state, file, true, 0)) {
+ goto out;
+ }
+ if (!posix_test_entry_check(state, symlnk_dangling, false, 0)) {
+ goto out;
+ }
+ if (!posix_test_entry_check(state, symlnk_outside_share, false, 0)) {
+ goto out;
+ }
+ if (!posix_test_entry_check(state, symlnk_in_share, true, 0)) {
+ goto out;
+ }
+
+ posix_test_entries_reset(state);
+
+ printf("Doing POSIX ls single\n");
+ state->flavour = POSIX;
+
+ cli_list(cli_unix, file, 0, posix_ls_fn, state);
+ cli_list(cli_unix, symlnk_dangling, 0, posix_ls_fn, state);
+ cli_list(cli_unix, symlnk_outside_share, 0, posix_ls_fn, state);
+ cli_list(cli_unix, symlnk_in_share, 0, posix_ls_fn, state);
+
+ if (!posix_test_entry_check(state, file, true, 0)) {
+ goto out;
+ }
+ if (!posix_test_entry_check(state,
+ symlnk_dangling,
+ true,
+ strlen(symlnk_dst_dangling)))
+ {
+ goto out;
+ }
+ if (!posix_test_entry_check(state,
+ symlnk_outside_share,
+ true,
+ strlen(symlnk_dst_outside_share)))
+ {
+ goto out;
+ }
+ if (!posix_test_entry_check(state,
+ symlnk_in_share,
+ true,
+ strlen(symlnk_dst_in_share))) {
+ goto out;
+ }
+
+ printf("POSIX-LS-SINGLE test passed\n");
+ correct = true;
+
+out:
+ cli_posix_unlink(cli_unix, file);
+ cli_posix_unlink(cli_unix, symlnk_dangling);
+ cli_posix_unlink(cli_unix, symlnk_in_share);
+ cli_posix_unlink(cli_unix, symlnk_outside_share);
+
+ if (!torture_close_connection(cli_unix)) {
+ correct = false;
+ }
+ if (!torture_close_connection(cli_win)) {
+ correct = false;
+ }
+
+ TALLOC_FREE(frame);
+ return correct;
+}
+
+/*
+ Test POSIX readlink of symlinks
+ */
+bool run_posix_readlink_test(int dummy)
+{
+ TALLOC_CTX *frame = NULL;
+ struct cli_state *cli_unix = NULL;
+ uint16_t fnum = (uint16_t)-1;
+ NTSTATUS status;
+ const char *file = "file";
+ const char *symlnk_dangling = "dangling";
+ const char *symlnk_dst_dangling = "xxxxxxx";
+ const char *symlnk_in_share = "symlnk_in_share";
+ const char *symlnk_dst_in_share = file;
+ const char *symlnk_outside_share = "symlnk_outside_share";
+ const char *symlnk_dst_outside_share = "/etc/passwd";
+ struct posix_test_entry entries[] = {
+ {
+ .name = symlnk_dangling,
+ .target = symlnk_dst_dangling,
+ .expected = symlnk_dangling,
+ }, {
+ .name = symlnk_in_share,
+ .target = symlnk_dst_in_share,
+ .expected = symlnk_in_share,
+ }, {
+ .name = symlnk_outside_share,
+ .target = symlnk_dst_outside_share,
+ .expected = symlnk_outside_share,
+ }, {
+ .name = NULL,
+ }
+ };
+ struct posix_test_state _state = {
+ .entries = &entries[0],
+ };
+ struct posix_test_state *state = &_state;
+ int i;
+ bool correct = false;
+
+ frame = talloc_stackframe();
+
+ printf("Starting POSIX-READLINK test\n");
+ state->flavour = POSIX;
+
+ if (!torture_open_connection(&cli_unix, 0)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ torture_conn_set_sockopt(cli_unix);
+
+ status = torture_setup_unix_extensions(cli_unix);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ cli_posix_unlink(cli_unix, file);
+ cli_posix_unlink(cli_unix, symlnk_dangling);
+ cli_posix_unlink(cli_unix, symlnk_in_share);
+ cli_posix_unlink(cli_unix, symlnk_outside_share);
+
+ status = cli_posix_open(cli_unix,
+ file,
+ O_RDWR|O_CREAT,
+ 0666,
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_open of %s failed error %s\n",
+ file,
+ nt_errstr(status));
+ goto out;
+ }
+
+ status = cli_close(cli_unix, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_close failed %s\n", nt_errstr(status));
+ goto out;
+ }
+ fnum = (uint16_t)-1;
+
+ for (i = 0; entries[i].name != NULL; i++) {
+ status = cli_posix_symlink(cli_unix,
+ entries[i].target,
+ entries[i].name);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("POSIX symlink of %s failed (%s)\n",
+ symlnk_dangling, nt_errstr(status));
+ goto out;
+ }
+ }
+
+ for (i = 0; entries[i].name != NULL; i++) {
+ char *target = NULL;
+
+ status = cli_readlink(
+ cli_unix,
+ entries[i].name,
+ talloc_tos(),
+ &target,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("POSIX readlink on %s failed (%s)\n",
+ entries[i].name, nt_errstr(status));
+ goto out;
+ }
+ if (strequal(target, entries[i].target)) {
+ entries[i].ok = true;
+ entries[i].returned_size = strlen(target);
+ }
+ }
+
+ if (!posix_test_entry_check(state,
+ symlnk_dangling,
+ true,
+ strlen(symlnk_dst_dangling)))
+ {
+ goto out;
+ }
+ if (!posix_test_entry_check(state,
+ symlnk_outside_share,
+ true,
+ strlen(symlnk_dst_outside_share)))
+ {
+ goto out;
+ }
+ if (!posix_test_entry_check(state,
+ symlnk_in_share,
+ true,
+ strlen(symlnk_dst_in_share))) {
+ goto out;
+ }
+
+ printf("POSIX-READLINK test passed\n");
+ correct = true;
+
+out:
+ cli_posix_unlink(cli_unix, file);
+ cli_posix_unlink(cli_unix, symlnk_dangling);
+ cli_posix_unlink(cli_unix, symlnk_in_share);
+ cli_posix_unlink(cli_unix, symlnk_outside_share);
+
+ if (!torture_close_connection(cli_unix)) {
+ correct = false;
+ }
+
+ TALLOC_FREE(frame);
+ return correct;
+}
+
+/*
+ Test POSIX stat of symlinks
+ */
+bool run_posix_stat_test(int dummy)
+{
+ TALLOC_CTX *frame = NULL;
+ struct cli_state *cli_unix = NULL;
+ uint16_t fnum = (uint16_t)-1;
+ NTSTATUS status;
+ const char *file = "file";
+ const char *symlnk_dangling = "dangling";
+ const char *symlnk_dst_dangling = "xxxxxxx";
+ const char *symlnk_in_share = "symlnk_in_share";
+ const char *symlnk_dst_in_share = file;
+ const char *symlnk_outside_share = "symlnk_outside_share";
+ const char *symlnk_dst_outside_share = "/etc/passwd";
+ struct posix_test_entry entries[] = {
+ {
+ .name = symlnk_dangling,
+ .target = symlnk_dst_dangling,
+ .expected = symlnk_dangling,
+ }, {
+ .name = symlnk_in_share,
+ .target = symlnk_dst_in_share,
+ .expected = symlnk_in_share,
+ }, {
+ .name = symlnk_outside_share,
+ .target = symlnk_dst_outside_share,
+ .expected = symlnk_outside_share,
+ }, {
+ .name = NULL,
+ }
+ };
+ struct posix_test_state _state = {
+ .entries = &entries[0],
+ };
+ struct posix_test_state *state = &_state;
+ int i;
+ bool correct = false;
+
+ frame = talloc_stackframe();
+
+ printf("Starting POSIX-STAT test\n");
+ state->flavour = POSIX;
+
+ if (!torture_open_connection(&cli_unix, 0)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ torture_conn_set_sockopt(cli_unix);
+
+ status = torture_setup_unix_extensions(cli_unix);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ cli_posix_unlink(cli_unix, file);
+ cli_posix_unlink(cli_unix, symlnk_dangling);
+ cli_posix_unlink(cli_unix, symlnk_in_share);
+ cli_posix_unlink(cli_unix, symlnk_outside_share);
+
+ status = cli_posix_open(cli_unix,
+ file,
+ O_RDWR|O_CREAT,
+ 0666,
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_open of %s failed error %s\n",
+ file,
+ nt_errstr(status));
+ goto out;
+ }
+
+ status = cli_close(cli_unix, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_close failed %s\n", nt_errstr(status));
+ goto out;
+ }
+ fnum = (uint16_t)-1;
+
+ for (i = 0; entries[i].name != NULL; i++) {
+ status = cli_posix_symlink(cli_unix,
+ entries[i].target,
+ entries[i].name);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("POSIX symlink of %s failed (%s)\n",
+ symlnk_dangling, nt_errstr(status));
+ goto out;
+ }
+ }
+
+ for (i = 0; entries[i].name != NULL; i++) {
+ SMB_STRUCT_STAT sbuf;
+
+ status = cli_posix_stat(cli_unix,
+ entries[i].name,
+ &sbuf);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("POSIX stat on %s failed (%s)\n",
+ entries[i].name, nt_errstr(status));
+ continue;
+ }
+ entries[i].ok = true;
+ entries[i].returned_size = sbuf.st_ex_size;
+ }
+
+ if (!posix_test_entry_check(state,
+ symlnk_dangling,
+ true,
+ strlen(symlnk_dst_dangling)))
+ {
+ goto out;
+ }
+ if (!posix_test_entry_check(state,
+ symlnk_outside_share,
+ true,
+ strlen(symlnk_dst_outside_share)))
+ {
+ goto out;
+ }
+ if (!posix_test_entry_check(state,
+ symlnk_in_share,
+ true,
+ strlen(symlnk_dst_in_share))) {
+ goto out;
+ }
+
+ printf("POSIX-STAT test passed\n");
+ correct = true;
+
+out:
+ cli_posix_unlink(cli_unix, file);
+ cli_posix_unlink(cli_unix, symlnk_dangling);
+ cli_posix_unlink(cli_unix, symlnk_in_share);
+ cli_posix_unlink(cli_unix, symlnk_outside_share);
+
+ if (!torture_close_connection(cli_unix)) {
+ correct = false;
+ }
+
+ TALLOC_FREE(frame);
+ return correct;
+}
+
+/*
+ Test Creating files and directories directly
+ under a symlink.
+ */
+bool run_posix_symlink_parent_test(int dummy)
+{
+ TALLOC_CTX *frame = NULL;
+ struct cli_state *cli_unix = NULL;
+ uint16_t fnum = (uint16_t)-1;
+ NTSTATUS status;
+ const char *parent_dir = "target_dir";
+ const char *parent_symlink = "symlink_to_target_dir";
+ const char *fname_real = "target_dir/file";
+ const char *dname_real = "target_dir/dir";
+ const char *fname_link = "symlink_to_target_dir/file";
+ const char *dname_link = "symlink_to_target_dir/dir";
+ const char *sname_link = "symlink_to_target_dir/symlink";
+ const char *hname_link = "symlink_to_target_dir/hardlink";
+ bool correct = false;
+
+ frame = talloc_stackframe();
+
+ printf("Starting POSIX-SYMLINK-PARENT test\n");
+
+ if (!torture_open_connection(&cli_unix, 0)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ torture_conn_set_sockopt(cli_unix);
+
+ status = torture_setup_unix_extensions(cli_unix);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ /* Start with a clean slate. */
+ cli_posix_unlink(cli_unix, fname_real);
+ cli_posix_rmdir(cli_unix, dname_real);
+ cli_posix_unlink(cli_unix, fname_link);
+ cli_posix_rmdir(cli_unix, dname_link);
+ cli_posix_unlink(cli_unix, sname_link);
+ cli_posix_unlink(cli_unix, hname_link);
+ cli_posix_unlink(cli_unix, parent_symlink);
+ cli_posix_rmdir(cli_unix, parent_dir);
+
+ /* Create parent_dir. */
+ status = cli_posix_mkdir(cli_unix, parent_dir, 0777);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_mkdir of %s failed error %s\n",
+ parent_dir,
+ nt_errstr(status));
+ goto out;
+ }
+ /* Create symlink to parent_dir. */
+ status = cli_posix_symlink(cli_unix,
+ parent_dir,
+ parent_symlink);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_symlink of %s -> %s failed error %s\n",
+ parent_symlink,
+ parent_dir,
+ nt_errstr(status));
+ goto out;
+ }
+ /* Try and create a directory under the symlink. */
+ status = cli_posix_mkdir(cli_unix, dname_link, 0777);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_mkdir of %s failed error %s\n",
+ dname_link,
+ nt_errstr(status));
+ goto out;
+ }
+ /* Try and create a file under the symlink. */
+ status = cli_posix_open(cli_unix,
+ fname_link,
+ O_RDWR|O_CREAT,
+ 0666,
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_open of %s failed error %s\n",
+ fname_link,
+ nt_errstr(status));
+ goto out;
+ }
+ status = cli_close(cli_unix, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_close failed %s\n", nt_errstr(status));
+ goto out;
+ }
+ fnum = (uint16_t)-1;
+
+ /* Try and create a symlink to the file under the symlink. */
+ status = cli_posix_symlink(cli_unix,
+ fname_link,
+ sname_link);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_symlink of %s -> %s failed error %s\n",
+ sname_link,
+ fname_link,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Try and create a hardlink to the file under the symlink. */
+ status = cli_posix_hardlink(cli_unix,
+ fname_link,
+ hname_link);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_hardlink of %s -> %s failed error %s\n",
+ hname_link,
+ fname_link,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Ensure we can delete the symlink via the parent symlink */
+ status = cli_posix_unlink(cli_unix, sname_link);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_unlink of %s failed error %s\n",
+ sname_link,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Ensure we can delete the hardlink via the parent symlink */
+ status = cli_posix_unlink(cli_unix, hname_link);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_unlink of %s failed error %s\n",
+ hname_link,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Ensure we can delete the directory via the parent symlink */
+ status = cli_posix_rmdir(cli_unix, dname_link);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_rmdir of %s failed error %s\n",
+ dname_link,
+ nt_errstr(status));
+ goto out;
+ }
+ /* Ensure we can delete the file via the parent symlink */
+ status = cli_posix_unlink(cli_unix, fname_link);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_unlink of %s failed error %s\n",
+ fname_link,
+ nt_errstr(status));
+ goto out;
+ }
+
+ printf("POSIX-SYMLINK-PARENT test passed\n");
+ correct = true;
+
+out:
+ if (fnum != (uint16_t)-1) {
+ cli_close(cli_unix, fnum);
+ }
+ cli_posix_unlink(cli_unix, fname_real);
+ cli_posix_rmdir(cli_unix, dname_real);
+ cli_posix_unlink(cli_unix, fname_link);
+ cli_posix_rmdir(cli_unix, dname_link);
+ cli_posix_unlink(cli_unix, sname_link);
+ cli_posix_unlink(cli_unix, hname_link);
+ cli_posix_unlink(cli_unix, parent_symlink);
+ cli_posix_rmdir(cli_unix, parent_dir);
+
+ if (!torture_close_connection(cli_unix)) {
+ correct = false;
+ }
+
+ TALLOC_FREE(frame);
+ return correct;
+}
+
+/*
+ Ensure we get an error when doing chmod on a symlink,
+ whether it is pointing to a real object or dangling.
+ */
+bool run_posix_symlink_chmod_test(int dummy)
+{
+ TALLOC_CTX *frame = NULL;
+ struct cli_state *cli_unix = NULL;
+ NTSTATUS status;
+ uint16_t fnum = (uint16_t)-1;
+ const char *fname_real = "file_real";
+ const char *fname_real_symlink = "file_real_symlink";
+ const char *nonexist = "nonexist";
+ const char *nonexist_symlink = "dangling_symlink";
+ bool correct = false;
+
+ frame = talloc_stackframe();
+
+ printf("Starting POSIX-SYMLINK-CHMOD test\n");
+
+ if (!torture_open_connection(&cli_unix, 0)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ torture_conn_set_sockopt(cli_unix);
+
+ status = torture_setup_unix_extensions(cli_unix);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ /* Start with a clean slate. */
+ cli_posix_unlink(cli_unix, fname_real);
+ cli_posix_unlink(cli_unix, fname_real_symlink);
+ cli_posix_unlink(cli_unix, nonexist);
+ cli_posix_unlink(cli_unix, nonexist_symlink);
+
+ /* Create a real file. */
+ status = cli_posix_open(cli_unix,
+ fname_real,
+ O_RDWR|O_CREAT,
+ 0644,
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_open of %s failed error %s\n",
+ fname_real,
+ nt_errstr(status));
+ goto out;
+ }
+ status = cli_close(cli_unix, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_close failed %s\n", nt_errstr(status));
+ goto out;
+ }
+ fnum = (uint16_t)-1;
+
+ /* Create symlink to real target. */
+ status = cli_posix_symlink(cli_unix,
+ fname_real,
+ fname_real_symlink);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_symlink of %s -> %s failed error %s\n",
+ fname_real_symlink,
+ fname_real,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* We should not be able to chmod symlinks that point to something. */
+ status = cli_posix_chmod(cli_unix, fname_real_symlink, 0777);
+
+ /* This should fail with something other than server crashed. */
+ if (NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_chmod of %s succeeded (should have failed)\n",
+ fname_real_symlink);
+ goto out;
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_DISCONNECTED)) {
+ /* Oops. Server crashed. */
+ printf("cli_posix_chmod of %s failed error %s\n",
+ fname_real_symlink,
+ nt_errstr(status));
+ goto out;
+ }
+ /* Any other failure is ok. */
+
+ /* Now create symlink to non-existing target. */
+ status = cli_posix_symlink(cli_unix,
+ nonexist,
+ nonexist_symlink);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_symlink of %s -> %s failed error %s\n",
+ nonexist_symlink,
+ nonexist,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* We should not be able to chmod symlinks that point to nothing. */
+ status = cli_posix_chmod(cli_unix, nonexist_symlink, 0777);
+
+ /* This should fail with something other than server crashed. */
+ if (NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_chmod of %s succeeded (should have failed)\n",
+ nonexist_symlink);
+ goto out;
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_DISCONNECTED)) {
+ /* Oops. Server crashed. */
+ printf("cli_posix_chmod of %s failed error %s\n",
+ nonexist_symlink,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Any other failure is ok. */
+ printf("POSIX-SYMLINK-CHMOD test passed (expected failure was %s)\n",
+ nt_errstr(status));
+ correct = true;
+
+out:
+ if (fnum != (uint16_t)-1) {
+ cli_close(cli_unix, fnum);
+ }
+ cli_posix_unlink(cli_unix, fname_real);
+ cli_posix_unlink(cli_unix, fname_real_symlink);
+ cli_posix_unlink(cli_unix, nonexist);
+ cli_posix_unlink(cli_unix, nonexist_symlink);
+
+ if (!torture_close_connection(cli_unix)) {
+ correct = false;
+ }
+
+ TALLOC_FREE(frame);
+ return correct;
+}
+
+/*
+ Ensure we get an ACL containing OI|IO ACE entries
+ after we add a default POSIX ACL to a directory.
+ This will only ever be an SMB1 test as it depends
+ on POSIX ACL semantics.
+ */
+bool run_posix_dir_default_acl_test(int dummy)
+{
+ TALLOC_CTX *frame = NULL;
+ struct cli_state *cli_unix = NULL;
+ NTSTATUS status;
+ uint16_t fnum = (uint16_t)-1;
+ const char *dname = "dir_with_default_acl";
+ bool correct = false;
+ SMB_STRUCT_STAT sbuf;
+ size_t acl_size = 0;
+ char *aclbuf = NULL;
+ size_t num_file_acls = 0;
+ size_t num_dir_acls = 0;
+ size_t expected_buflen;
+ uint8_t def_acl[SMB_POSIX_ACL_HEADER_SIZE +
+ 5*SMB_POSIX_ACL_ENTRY_SIZE] = {0};
+ uint8_t *p = NULL;
+ uint32_t i = 0;
+ struct security_descriptor *sd = NULL;
+ bool got_inherit = false;
+
+ frame = talloc_stackframe();
+
+ printf("Starting POSIX-DIR-DEFAULT-ACL test\n");
+
+ if (!torture_open_connection(&cli_unix, 0)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ torture_conn_set_sockopt(cli_unix);
+
+ status = torture_setup_unix_extensions(cli_unix);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ /* Start with a clean slate. */
+ cli_posix_unlink(cli_unix, dname);
+ cli_posix_rmdir(cli_unix, dname);
+
+ status = cli_posix_mkdir(cli_unix, dname, 0777);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_mkdir of %s failed error %s\n",
+ dname,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Do a posix stat to get the owner. */
+ status = cli_posix_stat(cli_unix, dname, &sbuf);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_stat of %s failed %s\n",
+ dname,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Get the ACL on the directory. */
+ status = cli_posix_getacl(cli_unix, dname, frame, &acl_size, &aclbuf);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_getacl on %s failed %s\n",
+ dname,
+ nt_errstr(status));
+ goto out;
+ }
+
+ if (acl_size < 6 || SVAL(aclbuf,0) != SMB_POSIX_ACL_VERSION) {
+ printf("%s, unknown POSIX acl version %u.\n",
+ dname,
+ (unsigned int)CVAL(aclbuf,0) );
+ goto out;
+ }
+
+ num_file_acls = SVAL(aclbuf,2);
+ num_dir_acls = SVAL(aclbuf,4);
+
+ /*
+ * No overflow check, num_*_acls comes from a 16-bit value,
+ * and we expect expected_buflen (size_t) to be of at least 32
+ * bit.
+ */
+ expected_buflen = SMB_POSIX_ACL_HEADER_SIZE +
+ SMB_POSIX_ACL_ENTRY_SIZE*(num_file_acls+num_dir_acls);
+
+ if (acl_size != expected_buflen) {
+ printf("%s, incorrect POSIX acl buffer size "
+ "(should be %zu, was %zu).\n",
+ dname,
+ expected_buflen,
+ acl_size);
+ goto out;
+ }
+
+ if (num_dir_acls != 0) {
+ printf("%s, POSIX default acl already exists"
+ "(should be 0, was %zu).\n",
+ dname,
+ num_dir_acls);
+ goto out;
+ }
+
+ /*
+ * Get the Windows ACL on the directory.
+ * Make sure there are no inheritable entries.
+ */
+ status = cli_ntcreate(cli_unix,
+ dname,
+ 0,
+ SEC_STD_READ_CONTROL,
+ 0,
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE,
+ FILE_OPEN,
+ FILE_DIRECTORY_FILE,
+ 0x0,
+ &fnum,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to open directory %s: %s\n",
+ dname,
+ nt_errstr(status));
+ goto out;
+ }
+
+ status = cli_query_security_descriptor(cli_unix,
+ fnum,
+ SECINFO_DACL,
+ frame,
+ &sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to get security descriptor on directory %s: %s\n",
+ dname,
+ nt_errstr(status));
+ goto out;
+ }
+
+ for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
+ struct security_ace *ace = &sd->dacl->aces[i];
+ if (ace->flags & (SEC_ACE_FLAG_OBJECT_INHERIT|
+ SEC_ACE_FLAG_CONTAINER_INHERIT)) {
+ printf("security descriptor on directory %s already "
+ "contains inheritance flags\n",
+ dname);
+ sec_desc_print(NULL, stdout, sd, true);
+ goto out;
+ }
+ }
+
+ TALLOC_FREE(sd);
+
+ /* Construct a new default ACL. */
+ SSVAL(def_acl,0,SMB_POSIX_ACL_VERSION);
+ SSVAL(def_acl,2,SMB_POSIX_IGNORE_ACE_ENTRIES);
+ SSVAL(def_acl,4,5); /* num_dir_acls. */
+
+ p = def_acl + SMB_POSIX_ACL_HEADER_SIZE;
+
+ /* USER_OBJ. */
+ SCVAL(p,0,SMB_POSIX_ACL_USER_OBJ); /* tagtype. */
+ SCVAL(p,1,SMB_POSIX_ACL_READ|SMB_POSIX_ACL_WRITE|SMB_POSIX_ACL_EXECUTE);
+ p += SMB_POSIX_ACL_ENTRY_SIZE;
+
+ /* GROUP_OBJ. */
+ SCVAL(p,0,SMB_POSIX_ACL_GROUP_OBJ); /* tagtype. */
+ SCVAL(p,1,SMB_POSIX_ACL_READ|SMB_POSIX_ACL_WRITE|SMB_POSIX_ACL_EXECUTE);
+ p += SMB_POSIX_ACL_ENTRY_SIZE;
+
+ /* OTHER. */
+ SCVAL(p,0,SMB_POSIX_ACL_OTHER); /* tagtype. */
+ SCVAL(p,1,SMB_POSIX_ACL_READ|SMB_POSIX_ACL_WRITE|SMB_POSIX_ACL_EXECUTE);
+ p += SMB_POSIX_ACL_ENTRY_SIZE;
+
+ /* Explicit user. */
+ SCVAL(p,0,SMB_POSIX_ACL_USER); /* tagtype. */
+ SCVAL(p,1,SMB_POSIX_ACL_READ|SMB_POSIX_ACL_WRITE|SMB_POSIX_ACL_EXECUTE);
+ SIVAL(p,2,sbuf.st_ex_uid);
+ p += SMB_POSIX_ACL_ENTRY_SIZE;
+
+ /* MASK. */
+ SCVAL(p,0,SMB_POSIX_ACL_MASK); /* tagtype. */
+ SCVAL(p,1,SMB_POSIX_ACL_READ|SMB_POSIX_ACL_WRITE|SMB_POSIX_ACL_EXECUTE);
+ p += SMB_POSIX_ACL_ENTRY_SIZE;
+
+ /* Set the POSIX default ACL. */
+ status = cli_posix_setacl(cli_unix, dname, def_acl, sizeof(def_acl));
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_setacl on %s failed %s\n",
+ dname,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /*
+ * Get the Windows ACL on the directory again.
+ * Now there should be inheritable entries.
+ */
+
+ status = cli_query_security_descriptor(cli_unix,
+ fnum,
+ SECINFO_DACL,
+ frame,
+ &sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed (2) to get security descriptor "
+ "on directory %s: %s\n",
+ dname,
+ nt_errstr(status));
+ goto out;
+ }
+
+ for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
+ struct security_ace *ace = &sd->dacl->aces[i];
+ if (ace->flags & (SEC_ACE_FLAG_OBJECT_INHERIT|
+ SEC_ACE_FLAG_CONTAINER_INHERIT)) {
+ got_inherit = true;
+ break;
+ }
+ }
+
+ if (!got_inherit) {
+ printf("security descriptor on directory %s does not "
+ "contain inheritance flags\n",
+ dname);
+ sec_desc_print(NULL, stdout, sd, true);
+ goto out;
+ }
+
+ cli_close(cli_unix, fnum);
+ fnum = (uint16_t)-1;
+ printf("POSIX-DIR-DEFAULT-ACL test passed\n");
+ correct = true;
+
+out:
+
+ TALLOC_FREE(sd);
+
+ if (fnum != (uint16_t)-1) {
+ cli_close(cli_unix, fnum);
+ }
+ cli_posix_unlink(cli_unix, dname);
+ cli_posix_rmdir(cli_unix, dname);
+
+ if (!torture_close_connection(cli_unix)) {
+ correct = false;
+ }
+
+ TALLOC_FREE(frame);
+ return correct;
+}
+
+/*
+ Ensure we can rename a symlink whether it is
+ pointing to a real object or dangling.
+ */
+bool run_posix_symlink_rename_test(int dummy)
+{
+ TALLOC_CTX *frame = NULL;
+ struct cli_state *cli_unix = NULL;
+ NTSTATUS status;
+ uint16_t fnum = (uint16_t)-1;
+ const char *fname_real = "file_real";
+ const char *fname_real_symlink = "file_real_symlink";
+ const char *fname_real_symlink_newname = "rename_file_real_symlink";
+ const char *nonexist = "nonexist";
+ const char *nonexist_symlink = "dangling_symlink";
+ const char *nonexist_symlink_newname = "dangling_symlink_rename";
+ bool correct = false;
+
+ frame = talloc_stackframe();
+
+ printf("Starting POSIX-SYMLINK-RENAME test\n");
+
+ if (!torture_open_connection(&cli_unix, 0)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ torture_conn_set_sockopt(cli_unix);
+
+ status = torture_setup_unix_extensions(cli_unix);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ /* Start with a clean slate. */
+ cli_posix_unlink(cli_unix, fname_real);
+ cli_posix_unlink(cli_unix, fname_real_symlink);
+ cli_posix_unlink(cli_unix, fname_real_symlink_newname);
+ cli_posix_unlink(cli_unix, nonexist);
+ cli_posix_unlink(cli_unix, nonexist_symlink);
+ cli_posix_unlink(cli_unix, nonexist_symlink_newname);
+
+ /* Create a real file. */
+ status = cli_posix_open(cli_unix,
+ fname_real,
+ O_RDWR|O_CREAT,
+ 0644,
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_open of %s failed error %s\n",
+ fname_real,
+ nt_errstr(status));
+ goto out;
+ }
+ status = cli_close(cli_unix, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_close failed %s\n", nt_errstr(status));
+ goto out;
+ }
+ fnum = (uint16_t)-1;
+
+ /* Create symlink to real target. */
+ status = cli_posix_symlink(cli_unix,
+ fname_real,
+ fname_real_symlink);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_symlink of %s -> %s failed error %s\n",
+ fname_real_symlink,
+ fname_real,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Ensure we can rename the symlink to the real file. */
+ status = cli_rename(cli_unix,
+ fname_real_symlink,
+ fname_real_symlink_newname,
+ false);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_rename of %s -> %s failed %s\n",
+ fname_real_symlink,
+ fname_real_symlink_newname,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Now create symlink to non-existing target. */
+ status = cli_posix_symlink(cli_unix,
+ nonexist,
+ nonexist_symlink);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_symlink of %s -> %s failed error %s\n",
+ nonexist_symlink,
+ nonexist,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Ensure we can rename the dangling symlink. */
+ status = cli_rename(cli_unix,
+ nonexist_symlink,
+ nonexist_symlink_newname,
+ false);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_rename of %s -> %s failed %s\n",
+ nonexist_symlink,
+ nonexist_symlink_newname,
+ nt_errstr(status));
+ goto out;
+ }
+
+ printf("POSIX-SYMLINK-RENAME test passed\n");
+ correct = true;
+
+out:
+ if (fnum != (uint16_t)-1) {
+ cli_close(cli_unix, fnum);
+ }
+ cli_posix_unlink(cli_unix, fname_real);
+ cli_posix_unlink(cli_unix, fname_real_symlink);
+ cli_posix_unlink(cli_unix, fname_real_symlink_newname);
+ cli_posix_unlink(cli_unix, nonexist);
+ cli_posix_unlink(cli_unix, nonexist_symlink);
+ cli_posix_unlink(cli_unix, nonexist_symlink_newname);
+
+ if (!torture_close_connection(cli_unix)) {
+ correct = false;
+ }
+
+ TALLOC_FREE(frame);
+ return correct;
+}
+
+/* List of info levels to try with a POSIX symlink path. */
+
+static struct {
+ uint32_t level;
+ const char *name;
+} posix_smb1_qpath_array[] = {
+ { SMB_INFO_STANDARD, "SMB_INFO_STANDARD"},
+ { SMB_INFO_QUERY_EA_SIZE, "SMB_INFO_QUERY_EA_SIZE"},
+ { SMB_INFO_IS_NAME_VALID, "SMB_INFO_IS_NAME_VALID"},
+ { SMB_INFO_QUERY_EAS_FROM_LIST, "SMB_INFO_QUERY_EAS_FROM_LIST"},
+ { SMB_INFO_QUERY_ALL_EAS, "SMB_INFO_QUERY_ALL_EAS"},
+ { SMB_FILE_BASIC_INFORMATION, "SMB_FILE_BASIC_INFORMATION"},
+ { SMB_FILE_STANDARD_INFORMATION, "SMB_FILE_STANDARD_INFORMATION"},
+ { SMB_FILE_EA_INFORMATION, "SMB_FILE_EA_INFORMATION"},
+ { SMB_FILE_ALTERNATE_NAME_INFORMATION,"SMB_FILE_ALTERNATE_NAME_INFORMATION"},
+ { SMB_QUERY_FILE_NAME_INFO, "SMB_QUERY_FILE_NAME_INFO"},
+ { SMB_FILE_NORMALIZED_NAME_INFORMATION,"SMB_FILE_NORMALIZED_NAME_INFORMATION"},
+ { SMB_FILE_ALLOCATION_INFORMATION, "SMB_FILE_ALLOCATION_INFORMATION"},
+ { SMB_FILE_END_OF_FILE_INFORMATION, "SMB_FILE_END_OF_FILE_INFORMATION"},
+ { SMB_FILE_ALL_INFORMATION, "SMB_FILE_ALL_INFORMATION"},
+ { SMB_FILE_INTERNAL_INFORMATION, "SMB_FILE_INTERNAL_INFORMATION"},
+ { SMB_FILE_ACCESS_INFORMATION, "SMB_FILE_ACCESS_INFORMATION"},
+ { SMB_FILE_NAME_INFORMATION, "SMB_FILE_NAME_INFORMATION"},
+ { SMB_FILE_DISPOSITION_INFORMATION, "SMB_FILE_DISPOSITION_INFORMATION"},
+ { SMB_FILE_POSITION_INFORMATION, "SMB_FILE_POSITION_INFORMATION"},
+ { SMB_FILE_MODE_INFORMATION, "SMB_FILE_MODE_INFORMATION"},
+ { SMB_FILE_ALIGNMENT_INFORMATION, "SMB_FILE_ALIGNMENT_INFORMATION"},
+ { SMB_FILE_STREAM_INFORMATION, "SMB_FILE_STREAM_INFORMATION"},
+ { SMB_FILE_COMPRESSION_INFORMATION, "SMB_FILE_COMPRESSION_INFORMATION"},
+ { SMB_FILE_NETWORK_OPEN_INFORMATION, "SMB_FILE_NETWORK_OPEN_INFORMATION"},
+ { SMB_FILE_ATTRIBUTE_TAG_INFORMATION, "SMB_FILE_ATTRIBUTE_TAG_INFORMATION"},
+ { SMB_QUERY_FILE_UNIX_BASIC, "SMB_QUERY_FILE_UNIX_BASIC"},
+ { SMB_QUERY_FILE_UNIX_INFO2, "SMB_QUERY_FILE_UNIX_INFO2"},
+ { SMB_QUERY_FILE_UNIX_LINK, "SMB_QUERY_FILE_UNIX_LINK"},
+ { SMB_QUERY_POSIX_ACL, "SMB_QUERY_POSIX_ACL"},
+ { SMB_QUERY_POSIX_LOCK, "SMB_QUERY_POSIX_LOCK"},
+};
+
+static NTSTATUS do_qpath(TALLOC_CTX *ctx,
+ struct cli_state *cli_unix,
+ const char *fname,
+ size_t i)
+{
+ NTSTATUS status;
+
+ if (posix_smb1_qpath_array[i].level ==
+ SMB_INFO_QUERY_EAS_FROM_LIST) {
+ uint16_t setup;
+ uint8_t *param;
+ uint8_t data[8];
+ uint8_t *rparam = NULL;
+ uint8_t *rdata = NULL;
+ uint32_t rbytes = 0;
+
+ /* Set up an EA list with 'a' as the single name. */
+ SIVAL(data,0, 8);
+ SCVAL(data,4, 2); /* namelen. */
+ SCVAL(data,5, 'a');
+ SCVAL(data,6, '\0'); /* name. */
+ SCVAL(data,7, '\0'); /* padding. */
+
+ SSVAL(&setup, 0, TRANSACT2_QPATHINFO);
+
+ param = talloc_zero_array(ctx, uint8_t, 6);
+ if (param == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ SSVAL(param, 0, SMB_INFO_QUERY_EAS_FROM_LIST);
+ param = trans2_bytes_push_str(param,
+ smbXcli_conn_use_unicode(cli_unix->conn),
+ fname,
+ strlen(fname)+1,
+ NULL);
+ if (param == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = cli_trans(ctx,
+ cli_unix,
+ SMBtrans2,
+ NULL,
+ -1,
+ 0,
+ 0,
+ &setup, 1, 0,
+ param, talloc_get_size(param), talloc_get_size(param),
+ data, 8, 0,
+ NULL,
+ NULL, 0, NULL,
+ &rparam, 0, &rbytes,
+ &rdata, 0, &rbytes);
+ TALLOC_FREE(rparam);
+ TALLOC_FREE(rdata);
+ } else {
+ uint8_t *rdata = NULL;
+ uint32_t num_rdata = 0;
+
+ status = cli_qpathinfo(ctx,
+ cli_unix,
+ fname,
+ posix_smb1_qpath_array[i].level,
+ 0, /* min_rdata */
+ 65534, /* max_rdata */
+ &rdata,
+ &num_rdata);
+ TALLOC_FREE(rdata);
+ }
+ /*
+ * We don't care what came back, so long as the
+ * server didn't crash.
+ */
+ if (NT_STATUS_EQUAL(status,
+ NT_STATUS_CONNECTION_DISCONNECTED)) {
+ printf("cli_qpathinfo of %s failed error "
+ "NT_STATUS_CONNECTION_DISCONNECTED\n",
+ fname);
+ return status;
+ }
+
+ printf("cli_qpathinfo info %x (%s) of %s got %s "
+ "(this is not an error)\n",
+ (unsigned int)posix_smb1_qpath_array[i].level,
+ posix_smb1_qpath_array[i].name,
+ fname,
+ nt_errstr(status));
+
+ return NT_STATUS_OK;
+}
+
+/*
+ Ensure we can call SMB1 getpathinfo in a symlink,
+ pointing to a real object or dangling. We mostly
+ expect errors, but the server must not crash.
+ */
+bool run_posix_symlink_getpathinfo_test(int dummy)
+{
+ TALLOC_CTX *frame = NULL;
+ struct cli_state *cli_unix = NULL;
+ NTSTATUS status;
+ uint16_t fnum = (uint16_t)-1;
+ const char *fname_real = "file_getpath_real";
+ const char *fname_real_symlink = "file_real_getpath_symlink";
+ const char *nonexist = "nonexist_getpath";
+ const char *nonexist_symlink = "dangling_getpath_symlink";
+ bool correct = false;
+ size_t i;
+
+ frame = talloc_stackframe();
+
+ printf("Starting POSIX-SYMLINK-GETPATHINFO test\n");
+
+ if (!torture_open_connection(&cli_unix, 0)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ torture_conn_set_sockopt(cli_unix);
+
+ status = torture_setup_unix_extensions(cli_unix);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ /* Start with a clean slate. */
+ cli_posix_unlink(cli_unix, fname_real);
+ cli_posix_unlink(cli_unix, fname_real_symlink);
+ cli_posix_unlink(cli_unix, nonexist);
+ cli_posix_unlink(cli_unix, nonexist_symlink);
+
+ /* Create a real file. */
+ status = cli_posix_open(cli_unix,
+ fname_real,
+ O_RDWR|O_CREAT,
+ 0644,
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_open of %s failed error %s\n",
+ fname_real,
+ nt_errstr(status));
+ goto out;
+ }
+ status = cli_close(cli_unix, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_close failed %s\n", nt_errstr(status));
+ goto out;
+ }
+ fnum = (uint16_t)-1;
+
+ /* Create symlink to real target. */
+ status = cli_posix_symlink(cli_unix,
+ fname_real,
+ fname_real_symlink);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_symlink of %s -> %s failed error %s\n",
+ fname_real_symlink,
+ fname_real,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Now create symlink to non-existing target. */
+ status = cli_posix_symlink(cli_unix,
+ nonexist,
+ nonexist_symlink);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_symlink of %s -> %s failed error %s\n",
+ nonexist_symlink,
+ nonexist,
+ nt_errstr(status));
+ goto out;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(posix_smb1_qpath_array); i++) {
+ status = do_qpath(frame,
+ cli_unix,
+ fname_real_symlink,
+ i);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+ status = do_qpath(frame,
+ cli_unix,
+ nonexist_symlink,
+ i);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+ }
+
+ printf("POSIX-SYMLINK-GETPATHINFO test passed\n");
+ correct = true;
+
+out:
+ if (fnum != (uint16_t)-1) {
+ cli_close(cli_unix, fnum);
+ }
+ cli_posix_unlink(cli_unix, fname_real);
+ cli_posix_unlink(cli_unix, fname_real_symlink);
+ cli_posix_unlink(cli_unix, nonexist);
+ cli_posix_unlink(cli_unix, nonexist_symlink);
+
+ if (!torture_close_connection(cli_unix)) {
+ correct = false;
+ }
+
+ TALLOC_FREE(frame);
+ return correct;
+}
+
+/* List of info levels to try with a POSIX symlink path. */
+
+static struct {
+ uint32_t level;
+ const char *name;
+ uint32_t data_len;
+} posix_smb1_setpath_array[] = {
+ { SMB_SET_FILE_UNIX_BASIC, "SMB_SET_FILE_UNIX_BASIC", 100},
+ { SMB_SET_FILE_UNIX_INFO2, "SMB_SET_FILE_UNIX_INFO2", 116},
+ { SMB_SET_FILE_UNIX_LINK, "SMB_SET_FILE_UNIX_LINK", 8},
+ { SMB_SET_FILE_UNIX_HLINK, "SMB_SET_FILE_UNIX_HLINK", 8},
+ { SMB_SET_POSIX_ACL, "SMB_SET_POSIX_ACL", 6},
+ { SMB_SET_POSIX_LOCK, "SMB_SET_POSIX_LOCK", 24},
+ { SMB_INFO_STANDARD, "SMB_INFO_STANDARD", 12},
+ { SMB_INFO_SET_EA, "SMB_INFO_SET_EA", 10},
+ { SMB_FILE_BASIC_INFORMATION, "SMB_FILE_BASIC_INFORMATION", 36},
+ { SMB_SET_FILE_ALLOCATION_INFO, "SMB_SET_FILE_ALLOCATION_INFO", 8},
+ { SMB_SET_FILE_END_OF_FILE_INFO,"SMB_SET_FILE_END_OF_FILE_INFO",8},
+ { SMB_SET_FILE_DISPOSITION_INFO,"SMB_SET_FILE_DISPOSITION_INFO",1},
+ { SMB_FILE_POSITION_INFORMATION,"SMB_FILE_POSITION_INFORMATION",8},
+ { SMB_FILE_FULL_EA_INFORMATION, "SMB_FILE_FULL_EA_INFORMATION",10},
+ { SMB_FILE_MODE_INFORMATION, "SMB_FILE_MODE_INFORMATION", 4},
+ { SMB_FILE_SHORT_NAME_INFORMATION,"SMB_FILE_SHORT_NAME_INFORMATION",12},
+ { SMB_FILE_RENAME_INFORMATION,"SMB_FILE_RENAME_INFORMATION", 20},
+ { SMB_FILE_LINK_INFORMATION, "SMB_FILE_LINK_INFORMATION", 20},
+};
+
+static NTSTATUS do_setpath(TALLOC_CTX *ctx,
+ struct cli_state *cli_unix,
+ const char *fname,
+ size_t i)
+{
+ NTSTATUS status;
+ uint8_t *data = NULL;
+
+ data = talloc_zero_array(ctx,
+ uint8_t,
+ posix_smb1_setpath_array[i].data_len);
+ if (data == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = cli_setpathinfo(cli_unix,
+ posix_smb1_setpath_array[i].level,
+ fname,
+ data,
+ posix_smb1_setpath_array[i].data_len);
+ TALLOC_FREE(data);
+
+ /*
+ * We don't care what came back, so long as the
+ * server didn't crash.
+ */
+ if (NT_STATUS_EQUAL(status,
+ NT_STATUS_CONNECTION_DISCONNECTED)) {
+ printf("cli_setpathinfo info %x (%s) of %s failed"
+ "error NT_STATUS_CONNECTION_DISCONNECTED\n",
+ (unsigned int)posix_smb1_setpath_array[i].level,
+ posix_smb1_setpath_array[i].name,
+ fname);
+ return status;
+ }
+
+ printf("cli_setpathinfo info %x (%s) of %s got %s "
+ "(this is not an error)\n",
+ (unsigned int)posix_smb1_setpath_array[i].level,
+ posix_smb1_setpath_array[i].name,
+ fname,
+ nt_errstr(status));
+
+ return NT_STATUS_OK;
+}
+
+/*
+ Ensure we can call SMB1 setpathinfo in a symlink,
+ pointing to a real object or dangling. We mostly
+ expect errors, but the server must not crash.
+ */
+bool run_posix_symlink_setpathinfo_test(int dummy)
+{
+ TALLOC_CTX *frame = NULL;
+ struct cli_state *cli_unix = NULL;
+ NTSTATUS status;
+ uint16_t fnum = (uint16_t)-1;
+ const char *fname_real = "file_setpath_real";
+ const char *fname_real_symlink = "file_real_setpath_symlink";
+ const char *nonexist = "nonexist_setpath";
+ const char *nonexist_symlink = "dangling_setpath_symlink";
+ bool correct = false;
+ size_t i;
+
+ frame = talloc_stackframe();
+
+ printf("Starting POSIX-SYMLINK-SETPATHINFO test\n");
+
+ if (!torture_open_connection(&cli_unix, 0)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ torture_conn_set_sockopt(cli_unix);
+
+ status = torture_setup_unix_extensions(cli_unix);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ /* Start with a clean slate. */
+ cli_posix_unlink(cli_unix, fname_real);
+ cli_posix_unlink(cli_unix, fname_real_symlink);
+ cli_posix_unlink(cli_unix, nonexist);
+ cli_posix_unlink(cli_unix, nonexist_symlink);
+
+ /* Create a real file. */
+ status = cli_posix_open(cli_unix,
+ fname_real,
+ O_RDWR|O_CREAT,
+ 0644,
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_open of %s failed error %s\n",
+ fname_real,
+ nt_errstr(status));
+ goto out;
+ }
+ status = cli_close(cli_unix, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_close failed %s\n", nt_errstr(status));
+ goto out;
+ }
+ fnum = (uint16_t)-1;
+
+ /* Create symlink to real target. */
+ status = cli_posix_symlink(cli_unix,
+ fname_real,
+ fname_real_symlink);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_symlink of %s -> %s failed error %s\n",
+ fname_real_symlink,
+ fname_real,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Now create symlink to non-existing target. */
+ status = cli_posix_symlink(cli_unix,
+ nonexist,
+ nonexist_symlink);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_posix_symlink of %s -> %s failed error %s\n",
+ nonexist_symlink,
+ nonexist,
+ nt_errstr(status));
+ goto out;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(posix_smb1_setpath_array); i++) {
+ status = do_setpath(frame,
+ cli_unix,
+ fname_real_symlink,
+ i);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+ status = do_setpath(frame,
+ cli_unix,
+ nonexist_symlink,
+ i);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+ }
+
+ printf("POSIX-SYMLINK-SETPATHINFO test passed\n");
+ correct = true;
+
+out:
+ if (fnum != (uint16_t)-1) {
+ cli_close(cli_unix, fnum);
+ }
+ cli_posix_unlink(cli_unix, fname_real);
+ cli_posix_unlink(cli_unix, fname_real_symlink);
+ cli_posix_unlink(cli_unix, nonexist);
+ cli_posix_unlink(cli_unix, nonexist_symlink);
+
+ if (!torture_close_connection(cli_unix)) {
+ correct = false;
+ }
+
+ TALLOC_FREE(frame);
+ return correct;
+}