[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