[PATCH] Add a "deltree" command to smbclient.
Jeremy Allison
jra at samba.org
Thu Jul 6 18:41:24 UTC 2017
Works over SMB1/2/3 and also in "posix" mode.
Once this is in we can use it to fix the flapping
symlink tests.
Includes test case + docs.
Please review and push if happy !
Cheers,
Jeremy.
-------------- next part --------------
From 960b509d2fc7138ebe4da8c897258cc7344df926 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Wed, 5 Jul 2017 15:53:07 -0700
Subject: [PATCH 1/4] s3: client: Move struct file_list code to using talloc
from malloc.
Signed-off-by: Jeremy Allison <jra at samba.org>
---
source3/client/client.c | 29 ++++++++++++++++++-----------
1 file changed, 18 insertions(+), 11 deletions(-)
diff --git a/source3/client/client.c b/source3/client/client.c
index c431a011466..82b31b9829d 100644
--- a/source3/client/client.c
+++ b/source3/client/client.c
@@ -2061,8 +2061,7 @@ static void free_file_list (struct file_list *l_head)
for (list = l_head; list; list = next) {
next = list->next;
DLIST_REMOVE(l_head, list);
- SAFE_FREE(list->file_path);
- SAFE_FREE(list);
+ TALLOC_FREE(list);
}
}
@@ -2107,8 +2106,11 @@ static int cmd_select(void)
match must be always set to true when calling this function
****************************************************************************/
-static int file_find(struct file_list **list, const char *directory,
- const char *expression, bool match)
+static int file_find(TALLOC_CTX *ctx,
+ struct file_list **list,
+ const char *directory,
+ const char *expression,
+ bool match)
{
DIR *dir;
struct file_list *entry;
@@ -2128,7 +2130,8 @@ static int file_find(struct file_list **list, const char *directory,
if (!strcmp(".", dname))
continue;
- if (asprintf(&path, "%s/%s", directory, dname) <= 0) {
+ path = talloc_asprintf("%s/%s", directory, dname);
+ if (path == NULL) {
continue;
}
@@ -2139,29 +2142,33 @@ static int file_find(struct file_list **list, const char *directory,
if (ret == 0) {
if (S_ISDIR(statbuf.st_mode)) {
isdir = true;
- ret = file_find(list, path, expression, false);
+ ret = file_find(ctx,
+ list,
+ path,
+ expression,
+ false);
}
} else {
d_printf("file_find: cannot stat file %s\n", path);
}
if (ret == -1) {
- SAFE_FREE(path);
+ TALLOC_FREE(path);
closedir(dir);
return -1;
}
}
- entry = SMB_MALLOC_P(struct file_list);
+ entry = talloc_zero(ctx, struct file_list);
if (!entry) {
d_printf("Out of memory in file_find\n");
closedir(dir);
return -1;
}
- entry->file_path = path;
+ entry->file_path = talloc_move(entry, &path);
entry->isdir = isdir;
DLIST_ADD(*list, entry);
} else {
- SAFE_FREE(path);
+ TALLOC_FREE(path);
}
}
@@ -2185,7 +2192,7 @@ static int cmd_mput(void)
file_list = NULL;
- ret = file_find(&file_list, ".", p, true);
+ ret = file_find(ctx, &file_list, ".", p, true);
if (ret) {
free_file_list(file_list);
continue;
--
2.13.2.725.g09c95d1e9-goog
From b6ecfca6b05de85ea8b6d5b6dcbf2fecd8f6e603 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Wed, 5 Jul 2017 17:21:18 -0700
Subject: [PATCH 2/4] s3: smbclient: Add new command deltree.
Signed-off-by: Jeremy Allison <jra at samba.org>
---
source3/client/client.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 175 insertions(+)
diff --git a/source3/client/client.c b/source3/client/client.c
index 82b31b9829d..d8c1a933970 100644
--- a/source3/client/client.c
+++ b/source3/client/client.c
@@ -2438,6 +2438,180 @@ static int cmd_del(void)
return 0;
}
+/****************************************************************************
+ Delete some files.
+****************************************************************************/
+
+static NTSTATUS delete_remote_files_list(struct cli_state *cli_state,
+ struct file_list *flist)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ struct file_list *deltree_list_iter = NULL;
+
+ for (deltree_list_iter = flist;
+ deltree_list_iter != NULL;
+ deltree_list_iter = deltree_list_iter->next) {
+ if (CLI_DIRSEP_CHAR == '/') {
+ /* POSIX. */
+ status = cli_posix_unlink(cli_state,
+ deltree_list_iter->file_path);
+ } else if (deltree_list_iter->isdir) {
+ status = cli_rmdir(cli_state,
+ deltree_list_iter->file_path);
+ } else {
+ status = cli_unlink(cli_state,
+ deltree_list_iter->file_path,
+ FILE_ATTRIBUTE_SYSTEM |
+ FILE_ATTRIBUTE_HIDDEN);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("%s deleting remote %s %s\n",
+ nt_errstr(status),
+ deltree_list_iter->isdir ?
+ "directory" : "file",
+ deltree_list_iter->file_path);
+ return status;
+ }
+ }
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Save a list of files to delete.
+****************************************************************************/
+
+static struct file_list *deltree_list_head;
+
+static NTSTATUS do_deltree_list(struct cli_state *cli_state,
+ struct file_info *finfo,
+ const char *dir)
+{
+ struct file_list **file_list_head_pp = &deltree_list_head;
+ struct file_list *dt = NULL;
+
+ if (!do_this_one(finfo)) {
+ return NT_STATUS_OK;
+ }
+
+ /* skip if this is . or .. */
+ if (ISDOT(finfo->name) || ISDOTDOT(finfo->name)) {
+ return NT_STATUS_OK;
+ }
+
+ dt = talloc_zero(NULL, struct file_list);
+ if (dt == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* create absolute filename for cli_ntcreate() */
+ dt->file_path = talloc_asprintf(dt,
+ "%s%s%s",
+ dir,
+ CLI_DIRSEP_STR,
+ finfo->name);
+ if (dt->file_path == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (finfo->mode & FILE_ATTRIBUTE_DIRECTORY) {
+ dt->isdir = true;
+ }
+
+ DLIST_ADD(*file_list_head_pp, dt);
+ return NT_STATUS_OK;
+}
+
+static int cmd_deltree(void)
+{
+ TALLOC_CTX *ctx = talloc_tos();
+ char *buf = NULL;
+ NTSTATUS status = NT_STATUS_OK;
+ struct file_list *deltree_list_norecurse = NULL;
+ struct file_list *deltree_list_iter = NULL;
+ uint16_t attribute = FILE_ATTRIBUTE_SYSTEM |
+ FILE_ATTRIBUTE_HIDDEN |
+ FILE_ATTRIBUTE_DIRECTORY;
+ char *mask = talloc_strdup(ctx, client_get_cur_dir());
+ if (!mask) {
+ return 1;
+ }
+ if (!next_token_talloc(ctx, &cmd_ptr,&buf,NULL)) {
+ d_printf("deltree <filename>\n");
+ return 1;
+ }
+ mask = talloc_asprintf_append(mask, "%s", buf);
+ if (!mask) {
+ return 1;
+ }
+
+ deltree_list_head = NULL;
+
+ /*
+ * Get the list of directories to
+ * delete (in case mask has a wildcard).
+ */
+ status = do_list(mask,attribute,do_deltree_list,false,true);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto err;
+ }
+ deltree_list_norecurse = deltree_list_head;
+ deltree_list_head = NULL;
+
+ for (deltree_list_iter = deltree_list_norecurse;
+ deltree_list_iter != NULL;
+ deltree_list_iter = deltree_list_iter->next) {
+
+ if (deltree_list_iter->isdir == false) {
+ /* Just a regular file. */
+ if (CLI_DIRSEP_CHAR == '/') {
+ /* POSIX. */
+ status = cli_posix_unlink(cli,
+ deltree_list_iter->file_path);
+ } else {
+ status = cli_unlink(cli,
+ deltree_list_iter->file_path,
+ FILE_ATTRIBUTE_SYSTEM |
+ FILE_ATTRIBUTE_HIDDEN);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ goto err;
+ }
+ continue;
+ }
+
+ /*
+ * Get the list of files or directories to
+ * delete in depth order.
+ */
+ status = do_list(deltree_list_iter->file_path,
+ attribute,
+ do_deltree_list,
+ true,
+ true);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto err;
+ }
+ status = delete_remote_files_list(cli, deltree_list_head);
+ free_file_list(deltree_list_head);
+ deltree_list_head = NULL;
+ if (!NT_STATUS_IS_OK(status)) {
+ goto err;
+ }
+ }
+
+ free_file_list(deltree_list_norecurse);
+ free_file_list(deltree_list_head);
+ return 0;
+
+ err:
+
+ free_file_list(deltree_list_norecurse);
+ free_file_list(deltree_list_head);
+ deltree_list_head = NULL;
+ return 1;
+}
+
+
/****************************************************************************
Wildcard delete some files.
****************************************************************************/
@@ -5005,6 +5179,7 @@ static struct {
{"chown",cmd_chown,"<src> <uid> <gid> chown a file using UNIX uids and gids",{COMPL_REMOTE,COMPL_NONE}},
{"close",cmd_close,"<fid> close a file given a fid",{COMPL_REMOTE,COMPL_NONE}},
{"del",cmd_del,"<mask> delete all matching files",{COMPL_REMOTE,COMPL_NONE}},
+ {"deltree",cmd_deltree,"<mask> recursively delete all matching files and directories",{COMPL_REMOTE,COMPL_NONE}},
{"dir",cmd_dir,"<mask> list the contents of the current directory",{COMPL_REMOTE,COMPL_NONE}},
{"du",cmd_du,"<mask> computes the total size of the current directory",{COMPL_REMOTE,COMPL_NONE}},
{"echo",cmd_echo,"ping the server",{COMPL_NONE,COMPL_NONE}},
--
2.13.2.725.g09c95d1e9-goog
From a0d4269b2cc416d5b900c006d23937237f181266 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Wed, 5 Jul 2017 17:23:48 -0700
Subject: [PATCH 3/4] docs: Document new smbclient deltree command.
Signed-off-by: Jeremy Allison <jra at samba.org>
---
docs-xml/manpages/smbclient.1.xml | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/docs-xml/manpages/smbclient.1.xml b/docs-xml/manpages/smbclient.1.xml
index 2b712fe18e6..432f60da78a 100644
--- a/docs-xml/manpages/smbclient.1.xml
+++ b/docs-xml/manpages/smbclient.1.xml
@@ -654,6 +654,16 @@
directory on the server. </para></listitem>
</varlistentry>
+ <varlistentry>
+ <term>deltree <mask></term>
+ <listitem><para>The client will request that the server attempt
+ to delete all files and directories matching <replaceable>mask</replaceable> from the current working
+ directory on the server. Note this will recursively delete files and directories within
+ the directories selected even without the recurse command being set. If any of the delete
+ requests fail the command will stop processing at that point, leaving files and directories
+ not yet processed untouched. This is by design.</para></listitem>
+ </varlistentry>
+
<varlistentry>
<term>dir <mask></term>
<listitem><para>A list of the files matching <replaceable>mask</replaceable> in the current
--
2.13.2.725.g09c95d1e9-goog
From d5162f6d9e078ebf6d86a2865800a0ca291b7fc7 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Thu, 6 Jul 2017 10:52:45 -0700
Subject: [PATCH 4/4] s3: tests: Add test for new smbclient "deltree" command.
Signed-off-by: Jeremy Allison <jra at samba.org>
---
source3/script/tests/test_smbclient_s3.sh | 47 +++++++++++++++++++++++++++++++
1 file changed, 47 insertions(+)
diff --git a/source3/script/tests/test_smbclient_s3.sh b/source3/script/tests/test_smbclient_s3.sh
index 4cfd054b018..6a022370205 100755
--- a/source3/script/tests/test_smbclient_s3.sh
+++ b/source3/script/tests/test_smbclient_s3.sh
@@ -1189,6 +1189,49 @@ EOF
fi
}
+# Test smbclient deltree command
+test_deltree()
+{
+ tmpfile=$PREFIX/smbclient_interactive_prompt_commands
+ deltree_dir=$PREFIX/deltree_dir
+
+ rm -f $deltree_dir
+ cat > $tmpfile <<EOF
+mkdir deltree_dir
+mkdir deltree_dir/foo
+mkdir deltree_dir/foo/bar
+put ${SMBCLIENT} deltree_dir/foo/bar/client
+deltree deltree_dir
+quit
+EOF
+ cmd='CLI_FORCE_INTERACTIVE=yes $SMBCLIENT "$@" -U$USERNAME%$PASSWORD //$SERVER/tmp -I $SERVER_IP $ADDARGS < $tmpfile 2>&1'
+ eval echo "$cmd"
+ out=`eval $cmd`
+ ret=$?
+
+ if [ $ret != 0 ] ; then
+ echo "$out"
+ echo "failed deltree test with output $ret"
+ false
+ return
+ fi
+
+ echo "$out" | grep 'NT_STATUS_'
+ ret=$?
+ if [ $ret -eq 0 ] ; then
+ echo "$out"
+ echo "failed - got an NT_STATUS error"
+ false
+ return
+ fi
+
+ if [ -d $deltree_dir ] ; then
+ echo "deltree did not delete everything"
+ false
+ return
+ fi
+}
+
test_server_os_message()
{
tmpfile=$PREFIX/smbclient_interactive_prompt_commands
@@ -1326,6 +1369,10 @@ testit "follow local symlinks" \
test_local_symlinks || \
failed=`expr $failed + 1`
+testit "smbclient deltree command" \
+ test_deltree || \
+ failed=`expr $failed + 1`
+
testit "server os message" \
test_server_os_message || \
failed=`expr $failed + 1`
--
2.13.2.725.g09c95d1e9-goog
More information about the samba-technical
mailing list