[openssh-commits] [openssh] 05/10: upstream: Add '-p' to sftp mkdir/lmkdir to create directories as
git+noreply at mindrot.org
git+noreply at mindrot.org
Tue Sep 15 15:38:09 AEST 2026
This is an automated email from the git hooks/post-receive script.
djm pushed a commit to branch master
in repository openssh.
commit d991f23d451c0c862ba7d14144f4ea44e1fb2a88
Author: job at openbsd.org <job at openbsd.org>
AuthorDate: Mon Sep 7 20:24:22 2026 +0000
upstream: Add '-p' to sftp mkdir/lmkdir to create directories as
required
The -p option causes the mkdir and lmkdir commands to create any missing
intermediate directories. If '-p' is specified, it is not considered an
error if a directory already exists.
With / OK djm@
OpenBSD-Commit-ID: 72e4d4cf2e254959af5a0885e3f3a176846f4345
---
misc.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++-
misc.h | 3 ++-
sftp-client.c | 35 ++++++++++++++++++++++++++++++++++-
sftp-client.h | 5 ++++-
sftp.1 | 22 ++++++++++++++++++----
sftp.c | 49 +++++++++++++++++++++++++++++++++++++++++++------
6 files changed, 149 insertions(+), 14 deletions(-)
diff --git a/misc.c b/misc.c
index 517fa7a97..ca8aecc67 100644
--- a/misc.c
+++ b/misc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: misc.c,v 1.215 2026/06/21 19:23:56 tb Exp $ */
+/* $OpenBSD: misc.c,v 1.216 2026/09/07 20:24:22 job Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
* Copyright (c) 2005-2020 Damien Miller. All rights reserved.
@@ -3227,3 +3227,50 @@ get_homedir(void)
return NULL;
}
+
+int
+mkdir_path(const char *target, mode_t mode)
+{
+ char *dir, *odir = NULL, *next;
+ int fd = AT_FDCWD, fd2, subpath_len, ret = -1;
+
+ dir = odir = xstrdup(target);
+
+ if (*dir == '/' &&
+ (fd = open("/", O_RDONLY|O_DIRECTORY)) == -1) {
+ error_f("open(\"/\"): %s", strerror(errno));
+ return -1;
+ }
+ /* Work through the path, component-wise */
+ for (; dir != NULL && *dir != '\0'; dir = next) {
+ if ((next = strchr(dir, '/')) != NULL)
+ *(next++) = '\0';
+ if (*dir == '\0')
+ continue;
+ subpath_len = (next == NULL) ? INT_MAX : next - odir - 1;
+ if (mkdirat(fd, dir, mode) == 0)
+ debug_f("created directory %.*s", subpath_len, target);
+ else if (errno != EEXIST) {
+ error_f("mkdir(\"%.*s\"): %s",
+ subpath_len, target, strerror(errno));
+ goto out;
+ }
+
+ /* descend */
+ if ((fd2 = openat(fd, dir, O_RDONLY|O_DIRECTORY)) == -1) {
+ error_f("open(\"%.*s\"): %s",
+ subpath_len, target, strerror(errno));
+ goto out;
+ }
+ if (fd != AT_FDCWD)
+ close(fd);
+ fd = fd2;
+ }
+ /* success */
+ ret = 0;
+ out:
+ free(odir);
+ if (fd != AT_FDCWD)
+ close(fd);
+ return ret;
+}
diff --git a/misc.h b/misc.h
index 791876c1e..c9afe0eca 100644
--- a/misc.h
+++ b/misc.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: misc.h,v 1.116 2026/03/11 09:10:59 dtucker Exp $ */
+/* $OpenBSD: misc.h,v 1.117 2026/09/07 20:24:22 job Exp $ */
/*
* Author: Tatu Ylonen <ylo at cs.hut.fi>
@@ -113,6 +113,7 @@ int path_absolute(const char *);
int stdfd_devnull(int, int, int);
int lib_contains_symbol(const char *, const char *);
char *get_homedir(void);
+int mkdir_path(const char *, mode_t);
void sock_set_v6only(int);
diff --git a/sftp-client.c b/sftp-client.c
index 1f031128e..e001b53da 100644
--- a/sftp-client.c
+++ b/sftp-client.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sftp-client.c,v 1.186 2026/06/29 01:53:21 djm Exp $ */
+/* $OpenBSD: sftp-client.c,v 1.187 2026/09/07 20:24:22 job Exp $ */
/*
* Copyright (c) 2001-2004 Damien Miller <djm at openbsd.org>
*
@@ -880,6 +880,39 @@ sftp_mkdir(struct sftp_conn *conn, const char *path, Attrib *a, int print_flag)
return status == SSH2_FX_OK ? 0 : -1;
}
+int
+sftp_mkpath(struct sftp_conn *conn, const char *path, Attrib *a, int print_flag)
+{
+ char *slash, *tmp_path;
+ int done;
+
+ tmp_path = xstrdup(path);
+ slash = tmp_path;
+
+ for (;;) {
+ slash += strspn(slash, "/");
+ slash += strcspn(slash, "/");
+
+ done = (*slash == '\0');
+ *slash = '\0';
+
+ if (!sftp_remote_is_dir(conn, tmp_path)) {
+ if (sftp_mkdir(conn, tmp_path, a, print_flag) != 0) {
+ free(tmp_path);
+ return -1;
+ }
+ }
+
+ if (done)
+ break;
+
+ *slash = '/';
+ }
+
+ free(tmp_path);
+ return 0;
+}
+
int
sftp_rmdir(struct sftp_conn *conn, const char *path)
{
diff --git a/sftp-client.h b/sftp-client.h
index cc8e20298..dc7c28e6c 100644
--- a/sftp-client.h
+++ b/sftp-client.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: sftp-client.h,v 1.41 2026/03/03 09:57:25 dtucker Exp $ */
+/* $OpenBSD: sftp-client.h,v 1.42 2026/09/07 20:24:22 job Exp $ */
/*
* Copyright (c) 2001-2004 Damien Miller <djm at openbsd.org>
@@ -87,6 +87,9 @@ int sftp_rm(struct sftp_conn *, const char *);
/* Create directory 'path' */
int sftp_mkdir(struct sftp_conn *, const char *, Attrib *, int);
+/* Create directory 'path' and intermediate directories as required */
+int sftp_mkpath(struct sftp_conn *, const char *, Attrib *, int);
+
/* Remove directory 'path' */
int sftp_rmdir(struct sftp_conn *, const char *);
diff --git a/sftp.1 b/sftp.1
index 651baaf85..a30338ba5 100644
--- a/sftp.1
+++ b/sftp.1
@@ -1,4 +1,4 @@
-.\" $OpenBSD: sftp.1,v 1.144 2024/12/06 15:12:56 djm Exp $
+.\" $OpenBSD: sftp.1,v 1.145 2026/09/07 20:24:22 job Exp $
.\"
.\" Copyright (c) 2001 Damien Miller. All rights reserved.
.\"
@@ -22,7 +22,7 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
-.Dd $Mdocdate: December 6 2024 $
+.Dd $Mdocdate: September 7 2026 $
.Dt SFTP 1
.Os
.Sh NAME
@@ -564,9 +564,16 @@ command.
may contain
.Xr glob 7
characters and may match multiple files.
-.It Ic lmkdir Ar path
+.It Xo Ic lmkdir
+.Op Fl p
+.Ar path
+.Xc
Create local directory specified by
.Ar path .
+If the
+.Fl p
+flag is specified create intermediate directories as required.
+Do not consider it an error if the argument directory already exists.
.It Xo Ic ln
.Op Fl s
.Ar oldpath
@@ -629,9 +636,16 @@ Sort the listing by last modification time.
.It Ic lumask Ar umask
Set local umask to
.Ar umask .
-.It Ic mkdir Ar path
+.It Xo Ic mkdir
+.Op Fl p
+.Ar path
+.Xc
Create remote directory specified by
.Ar path .
+If the
+.Fl p
+flag is specified create intermediate directories as required.
+Do not consider it an error if the argument directory already exists.
.It Ic progress
Toggle display of progress meter.
.It Xo Ic put
diff --git a/sftp.c b/sftp.c
index 31d8fa494..eba4acfdd 100644
--- a/sftp.c
+++ b/sftp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sftp.c,v 1.257 2026/06/30 02:30:19 djm Exp $ */
+/* $OpenBSD: sftp.c,v 1.258 2026/09/07 20:24:22 job Exp $ */
/*
* Copyright (c) 2001-2004 Damien Miller <djm at openbsd.org>
*
@@ -288,12 +288,12 @@ help(void)
"help Display this help text\n"
"lcd path Change local directory to 'path'\n"
"lls [ls-options [path]] Display local directory listing\n"
- "lmkdir path Create local directory\n"
+ "lmkdir [-p] path Create local directory\n"
"ln [-s] oldpath newpath Link remote file (-s for symlink)\n"
"lpwd Print local working directory\n"
"ls [-1afhlnrSt] [path] Display remote directory listing\n"
"lumask umask Set local umask to 'umask'\n"
- "mkdir path Create remote directory\n"
+ "mkdir [-p] path Create remote directory\n"
"progress Toggle display of progress meter\n"
"put [-afpR] local [remote] Upload file\n"
"pwd Display remote working directory\n"
@@ -417,6 +417,30 @@ parse_getput_flags(const char *cmd, char **argv, int argc,
return optind;
}
+static int
+parse_mkdir_flags(const char *cmd, char **argv, int argc, int *pflag)
+{
+ extern int opterr, optind, optopt, optreset;
+ int ch;
+
+ optind = optreset = 1;
+ opterr = 0;
+
+ *pflag = 0;
+ while ((ch = getopt(argc, argv, "p")) != -1) {
+ switch (ch) {
+ case 'p':
+ *pflag = 1;
+ break;
+ default:
+ error("%s: Invalid flag -%c", cmd, optopt);
+ return -1;
+ }
+ }
+
+ return optind;
+}
+
static int
parse_link_flags(const char *cmd, char **argv, int argc, int *sflag)
{
@@ -1455,16 +1479,21 @@ parse_args(const char **cpp, int *ignore_errors, int *disable_echo, int *aflag,
undo_glob_escape(*path1);
undo_glob_escape(*path2);
break;
- case I_RM:
case I_MKDIR:
- case I_RMDIR:
case I_LMKDIR:
+ if ((optidx = parse_mkdir_flags(cmd, argv, argc, pflag)) == -1)
+ return -1;
+ path1_mandatory = 1;
+ goto parse_one_path;
+ case I_RM:
+ case I_RMDIR:
path1_mandatory = 1;
/* FALLTHROUGH */
case I_CHDIR:
case I_LCHDIR:
if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
return -1;
+ parse_one_path:
/* Get pathname (mandatory) */
if (argc - optidx < 1) {
if (!path1_mandatory)
@@ -1639,7 +1668,10 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
attrib_clear(&a);
a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
a.perm = 0777;
- err = sftp_mkdir(conn, path1, &a, 1);
+ if (pflag)
+ err = sftp_mkpath(conn, path1, &a, 1);
+ else
+ err = sftp_mkdir(conn, path1, &a, 1);
break;
case I_RMDIR:
path1 = sftp_make_absolute(path1, *pwd);
@@ -1708,6 +1740,11 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
}
break;
case I_LMKDIR:
+ if (pflag) {
+ if (mkdir_path(path1, 0777) != 0)
+ err = 1;
+ break;
+ }
if (mkdir(path1, 0777) == -1) {
error("Couldn't create local directory "
"\"%s\": %s", path1, strerror(errno));
--
To stop receiving notification emails like this one, please contact
djm at mindrot.org.
More information about the openssh-commits
mailing list