From b0e7307b3569a5dad0f2606d2736cc8317851598 Mon Sep 17 00:00:00 2001
From: Dominique Martinet <dominique.martinet@atmark-techno.com>
Date: Wed, 30 Aug 2023 11:46:01 +0900
Subject: [PATCH 1/2] utils: add mkdir_recursive

This will be used in the next commit.

A test file for utils has also been added to check mkdir works as
intended.

Signed-off-by: Dominique Martinet <dominique.martinet@atmark-techno.com>
---
 pppd/Makefile.am    |   6 ++
 pppd/pppd-private.h |   1 +
 pppd/utils.c        |  82 ++++++++++++++++++++++++++
 pppd/utils_utest.c  | 139 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 228 insertions(+)
 create mode 100644 pppd/utils_utest.c

--- pppd/Makefile.am.orig
+++ pppd/Makefile.am
@@ -20,6 +20,12 @@ utest_pppcrypt_LDFLAGS =
 
 check_PROGRAMS += utest_crypto
 
+utest_utils_SOURCES = utils.c utils_utest.c
+utest_utils_CPPFLAGS = -DUNIT_TEST
+utest_utils_LDFLAGS =
+
+check_PROGRAMS += utest_utils
+
 if WITH_SRP
 sbin_PROGRAMS += srp-entry
 dist_man8_MANS += srp-entry.8
--- pppd/pppd-private.h.orig
+++ pppd/pppd-private.h
@@ -437,6 +437,7 @@ int  sifproxyarp(int, u_int32_t);
 int  cifproxyarp(int, u_int32_t);
 				/* Delete proxy ARP entry for peer */
 u_int32_t GetMask(u_int32_t); /* Get appropriate netmask for address */
+int  mkdir_recursive(const char *); /* Recursively create directory */
 int  lock(char *);	/* Create lock file for device */
 int  relock(int);		/* Rewrite lock file with new pid */
 void unlock(void);	/* Delete previously-created lock file */
--- pppd/utils.c.orig
+++ pppd/utils.c
@@ -781,6 +781,88 @@ complete_read(int fd, void *buf, size_t
 }
 #endif
 
+/*
+ * mkdir_check - helper for mkdir_recursive, creates a directory
+ * but do not error on EEXIST if and only if entry is a directory
+ * The caller must check for errno == ENOENT if appropriate.
+ */
+static int
+mkdir_check(const char *path)
+{
+    struct stat statbuf;
+
+    if (mkdir(path, 0755) >= 0)
+	return 0;
+
+    if (errno == EEXIST) {
+	if (stat(path, &statbuf) < 0)
+	    /* got raced? */
+	    return -1;
+
+	if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
+	    return 0;
+
+	/* already exists but not a dir, treat as failure */
+	errno = EEXIST;
+	return -1;
+    }
+
+    return -1;
+}
+
+/*
+ * mkdir_parent - helper for mkdir_recursive, modifies the string in place
+ * Assumes mkdir(path) already failed, so it first creates the parent then
+ * full path again.
+ */
+static int
+mkdir_parent(char *path)
+{
+    char *slash;
+    int rc;
+
+    slash = strrchr(path, '/');
+    if (!slash)
+	return -1;
+
+    *slash = 0;
+    if (mkdir_check(path) < 0) {
+	if (errno != ENOENT) {
+	    *slash = '/';
+	    return -1;
+	}
+	if (mkdir_parent(path) < 0) {
+	    *slash = '/';
+	    return -1;
+	}
+    }
+    *slash = '/';
+
+    return mkdir_check(path);
+}
+
+/*
+ * mkdir_recursive - recursively create directory if it didn't exist
+ */
+int
+mkdir_recursive(const char *path)
+{
+    char *copy;
+    int rc;
+
+    // optimistically try on full path first to avoid allocation
+    if (mkdir_check(path) == 0)
+	return 0;
+
+    copy = strdup(path);
+    if (!copy)
+	return -1;
+
+    rc = mkdir_parent(copy);
+    free(copy);
+    return rc;
+}
+
 /* Procedures for locking the serial device using a lock file. */
 static char lock_file[MAXPATHLEN];
 
--- /dev/null
+++ pppd/utils_utest.c
@@ -0,0 +1,139 @@
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "pppd-private.h"
+
+/* globals used in test.c... */
+int debug = 1;
+int error_count;
+int unsuccess;
+
+/* check if path exists and returns its type */
+static int
+file_type(char *path)
+{
+    struct stat statbuf;
+
+    if (stat(path, &statbuf) < 0)
+	return -1;
+
+    return statbuf.st_mode & S_IFMT;
+}
+
+int
+test_simple() {
+    if (mkdir_recursive("dir"))
+	return -1;
+
+    if (file_type("dir") != S_IFDIR)
+	return -1;
+
+    rmdir("dir");
+    return 0;
+}
+
+int
+test_recurse() {
+    if (mkdir_recursive("dir/subdir/subsubdir"))
+	return -1;
+
+    if (file_type("dir/subdir/subsubdir") != S_IFDIR)
+	return -1;
+
+    rmdir("dir/subdir/subsubdir");
+
+    /* try again with partial existence */
+    if (mkdir_recursive("dir/subdir/subsubdir"))
+	return -1;
+
+    if (file_type("dir/subdir/subsubdir") != S_IFDIR)
+	return -1;
+
+    rmdir("dir/subdir/subsubdir");
+    rmdir("dir/subdir");
+    rmdir("dir");
+    return 0;
+}
+
+int
+test_recurse_multislash() {
+    if (mkdir_recursive("dir/subdir///subsubdir"))
+	return -1;
+
+    if (file_type("dir/subdir/subsubdir") != S_IFDIR)
+	return -1;
+
+    rmdir("dir/subdir/subsubdir");
+    rmdir("dir/subdir");
+
+    /* try again with partial existence */
+    if (mkdir_recursive("dir/subdir/subsubdir///"))
+	return -1;
+
+    if (file_type("dir/subdir/subsubdir") != S_IFDIR)
+	return -1;
+
+    rmdir("dir/subdir/subsubdir");
+    rmdir("dir/subdir");
+    rmdir("dir");
+    return 0;
+}
+
+int
+test_parent_notdir() {
+    int fd = open("file", O_CREAT, 0600);
+    if (fd < 0)
+	return -1;
+    close(fd);
+
+    if (mkdir_recursive("file") == 0)
+	return -1;
+    if (mkdir_recursive("file/dir") == 0)
+	return -1;
+
+    unlink("file");
+    return 0;
+}
+
+int
+main()
+{
+    char *base_dir = strdup("/tmp/ppp_utils_utest.XXXXXX");
+    int failure = 0;
+
+    if (mkdtemp(base_dir) == NULL) {
+	printf("Could not create test directory, aborting\n");
+	return 1;
+    }
+
+    if (chdir(base_dir) < 0) {
+	printf("Could not enter newly created test dir, aborting\n");
+	return 1;
+    }
+
+    if (test_simple()) {
+	printf("Could not create simple directory\n");
+	failure++;
+    }
+
+    if (test_recurse()) {
+	printf("Could not create recursive directory\n");
+	failure++;
+    }
+
+    if (test_recurse_multislash()) {
+	printf("Could not create recursive directory with multiple slashes\n");
+	failure++;
+    }
+
+    if (test_parent_notdir()) {
+	printf("Creating over a file appeared to work?\n");
+	failure++;
+    }
+
+    rmdir(base_dir);
+    free(base_dir);
+    return failure;
+}
--- pppd/tdb.c.orig
+++ pppd/tdb.c
@@ -60,8 +60,11 @@
 #include <sys/mman.h>
 #include <sys/stat.h>
 #include <signal.h>
+
+#include "pppd-private.h"
 #include "tdb.h"
 #include "spinlock.h"
+#include "pathnames.h"
 
 #define TDB_MAGIC_FOOD "TDB file\n"
 #define TDB_VERSION (0x26011967 + 6)
@@ -1728,7 +1731,12 @@ TDB_CONTEXT *tdb_open_ex(const char *nam
 		goto internal;
 	}
 
+again:
 	if ((tdb->fd = open(name, open_flags, mode)) == -1) {
+		if ((open_flags & O_CREAT) && errno == ENOENT &&
+			mkdir_recursive(PPP_PATH_VARRUN) == 0)
+			goto again;
+
 		TDB_LOG((tdb, 5, "tdb_open_ex: could not open file %s: %s\n",
 			 name, strerror(errno)));
 		goto fail;	/* errno set by open(2) */
--- pppd/Makefile.in.orig
+++ pppd/Makefile.in
@@ -92,8 +92,8 @@ POST_UNINSTALL = :
 build_triplet = @build@
 host_triplet = @host@
 sbin_PROGRAMS = pppd$(EXEEXT) $(am__EXEEXT_4)
-check_PROGRAMS = utest_crypto$(EXEEXT) $(am__EXEEXT_1) $(am__EXEEXT_2) \
-	$(am__EXEEXT_3)
+check_PROGRAMS = utest_crypto$(EXEEXT) utest_utils$(EXEEXT) \
+	$(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3)
 @WITH_SRP_TRUE@am__append_1 = srp-entry
 @WITH_SRP_TRUE@am__append_2 = srp-entry.8
 @PPP_WITH_SYSTEM_CA_PATH_TRUE@am__append_3 = -DSYSTEM_CA_PATH='"@SYSTEM_CA_PATH@"'
@@ -258,6 +258,13 @@ utest_pppcrypt_LINK = $(LIBTOOL) $(AM_V_
 	$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
 	$(AM_CFLAGS) $(CFLAGS) $(utest_pppcrypt_LDFLAGS) $(LDFLAGS) -o \
 	$@
+am_utest_utils_OBJECTS = utest_utils-utils.$(OBJEXT) \
+	utest_utils-utils_utest.$(OBJEXT)
+utest_utils_OBJECTS = $(am_utest_utils_OBJECTS)
+utest_utils_LDADD = $(LDADD)
+utest_utils_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+	$(utest_utils_LDFLAGS) $(LDFLAGS) -o $@
 AM_V_P = $(am__v_P_@AM_V@)
 am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
 am__v_P_0 = false
@@ -300,7 +307,9 @@ am__depfiles_remade = ./$(DEPDIR)/libppp
 	./$(DEPDIR)/utest_crypto-crypto.Po \
 	./$(DEPDIR)/utest_peap-mppe.Po ./$(DEPDIR)/utest_peap-peap.Po \
 	./$(DEPDIR)/utest_peap-utils.Po \
-	./$(DEPDIR)/utest_pppcrypt-crypto_ms.Po
+	./$(DEPDIR)/utest_pppcrypt-crypto_ms.Po \
+	./$(DEPDIR)/utest_utils-utils.Po \
+	./$(DEPDIR)/utest_utils-utils_utest.Po
 am__mv = mv -f
 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
 	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
@@ -323,11 +332,11 @@ am__v_CCLD_1 =
 SOURCES = $(libppp_crypto_la_SOURCES) $(pppd_SOURCES) \
 	$(srp_entry_SOURCES) $(utest_chap_SOURCES) \
 	$(utest_crypto_SOURCES) $(utest_peap_SOURCES) \
-	$(utest_pppcrypt_SOURCES)
+	$(utest_pppcrypt_SOURCES) $(utest_utils_SOURCES)
 DIST_SOURCES = $(libppp_crypto_la_SOURCES) $(am__pppd_SOURCES_DIST) \
 	$(am__srp_entry_SOURCES_DIST) $(utest_chap_SOURCES) \
 	$(utest_crypto_SOURCES) $(utest_peap_SOURCES) \
-	$(utest_pppcrypt_SOURCES)
+	$(utest_pppcrypt_SOURCES) $(utest_utils_SOURCES)
 am__can_run_installinfo = \
   case $$AM_UPDATE_INFO_DIR in \
     n|no|NO) false;; \
@@ -733,6 +742,9 @@ utest_crypto_LDFLAGS =
 utest_pppcrypt_SOURCES = crypto_ms.c
 utest_pppcrypt_CPPFLAGS = -DUNIT_TEST_MSCRYPTO
 utest_pppcrypt_LDFLAGS = 
+utest_utils_SOURCES = utils.c utils_utest.c
+utest_utils_CPPFLAGS = -DUNIT_TEST
+utest_utils_LDFLAGS = 
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = pppd.pc
 pppd_includedir = $(includedir)/pppd
@@ -955,6 +967,10 @@ utest_pppcrypt$(EXEEXT): $(utest_pppcryp
 	@rm -f utest_pppcrypt$(EXEEXT)
 	$(AM_V_CCLD)$(utest_pppcrypt_LINK) $(utest_pppcrypt_OBJECTS) $(utest_pppcrypt_LDADD) $(LIBS)
 
+utest_utils$(EXEEXT): $(utest_utils_OBJECTS) $(utest_utils_DEPENDENCIES) $(EXTRA_utest_utils_DEPENDENCIES) 
+	@rm -f utest_utils$(EXEEXT)
+	$(AM_V_CCLD)$(utest_utils_LINK) $(utest_utils_OBJECTS) $(utest_utils_LDADD) $(LIBS)
+
 mostlyclean-compile:
 	-rm -f *.$(OBJEXT)
 
@@ -1006,6 +1022,8 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utest_peap-peap.Po@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utest_peap-utils.Po@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utest_pppcrypt-crypto_ms.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utest_utils-utils.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utest_utils-utils_utest.Po@am__quote@ # am--include-marker
 
 $(am__depfiles_remade):
 	@$(MKDIR_P) $(@D)
@@ -1629,6 +1647,34 @@ utest_pppcrypt-crypto_ms.obj: crypto_ms.
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(utest_pppcrypt_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utest_pppcrypt-crypto_ms.obj `if test -f 'crypto_ms.c'; then $(CYGPATH_W) 'crypto_ms.c'; else $(CYGPATH_W) '$(srcdir)/crypto_ms.c'; fi`
 
+utest_utils-utils.o: utils.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(utest_utils_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utest_utils-utils.o -MD -MP -MF $(DEPDIR)/utest_utils-utils.Tpo -c -o utest_utils-utils.o `test -f 'utils.c' || echo '$(srcdir)/'`utils.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/utest_utils-utils.Tpo $(DEPDIR)/utest_utils-utils.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='utils.c' object='utest_utils-utils.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(utest_utils_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utest_utils-utils.o `test -f 'utils.c' || echo '$(srcdir)/'`utils.c
+
+utest_utils-utils.obj: utils.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(utest_utils_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utest_utils-utils.obj -MD -MP -MF $(DEPDIR)/utest_utils-utils.Tpo -c -o utest_utils-utils.obj `if test -f 'utils.c'; then $(CYGPATH_W) 'utils.c'; else $(CYGPATH_W) '$(srcdir)/utils.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/utest_utils-utils.Tpo $(DEPDIR)/utest_utils-utils.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='utils.c' object='utest_utils-utils.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(utest_utils_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utest_utils-utils.obj `if test -f 'utils.c'; then $(CYGPATH_W) 'utils.c'; else $(CYGPATH_W) '$(srcdir)/utils.c'; fi`
+
+utest_utils-utils_utest.o: utils_utest.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(utest_utils_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utest_utils-utils_utest.o -MD -MP -MF $(DEPDIR)/utest_utils-utils_utest.Tpo -c -o utest_utils-utils_utest.o `test -f 'utils_utest.c' || echo '$(srcdir)/'`utils_utest.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/utest_utils-utils_utest.Tpo $(DEPDIR)/utest_utils-utils_utest.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='utils_utest.c' object='utest_utils-utils_utest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(utest_utils_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utest_utils-utils_utest.o `test -f 'utils_utest.c' || echo '$(srcdir)/'`utils_utest.c
+
+utest_utils-utils_utest.obj: utils_utest.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(utest_utils_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utest_utils-utils_utest.obj -MD -MP -MF $(DEPDIR)/utest_utils-utils_utest.Tpo -c -o utest_utils-utils_utest.obj `if test -f 'utils_utest.c'; then $(CYGPATH_W) 'utils_utest.c'; else $(CYGPATH_W) '$(srcdir)/utils_utest.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/utest_utils-utils_utest.Tpo $(DEPDIR)/utest_utils-utils_utest.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='utils_utest.c' object='utest_utils-utils_utest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(utest_utils_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utest_utils-utils_utest.obj `if test -f 'utils_utest.c'; then $(CYGPATH_W) 'utils_utest.c'; else $(CYGPATH_W) '$(srcdir)/utils_utest.c'; fi`
+
 mostlyclean-libtool:
 	-rm -f *.lo
 
@@ -1918,6 +1964,13 @@ utest_crypto.log: utest_crypto$(EXEEXT)
 	--log-file $$b.log --trs-file $$b.trs \
 	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
 	"$$tst" $(AM_TESTS_FD_REDIRECT)
+utest_utils.log: utest_utils$(EXEEXT)
+	@p='utest_utils$(EXEEXT)'; \
+	b='utest_utils'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
 utest_chap.log: utest_chap$(EXEEXT)
 	@p='utest_chap$(EXEEXT)'; \
 	b='utest_chap'; \
@@ -2080,6 +2133,8 @@ distclean: distclean-am
 	-rm -f ./$(DEPDIR)/utest_peap-peap.Po
 	-rm -f ./$(DEPDIR)/utest_peap-utils.Po
 	-rm -f ./$(DEPDIR)/utest_pppcrypt-crypto_ms.Po
+	-rm -f ./$(DEPDIR)/utest_utils-utils.Po
+	-rm -f ./$(DEPDIR)/utest_utils-utils_utest.Po
 	-rm -f Makefile
 distclean-am: clean-am distclean-compile distclean-generic \
 	distclean-hdr distclean-tags
@@ -2171,6 +2226,8 @@ maintainer-clean: maintainer-clean-am
 	-rm -f ./$(DEPDIR)/utest_peap-peap.Po
 	-rm -f ./$(DEPDIR)/utest_peap-utils.Po
 	-rm -f ./$(DEPDIR)/utest_pppcrypt-crypto_ms.Po
+	-rm -f ./$(DEPDIR)/utest_utils-utils.Po
+	-rm -f ./$(DEPDIR)/utest_utils-utils_utest.Po
 	-rm -f Makefile
 maintainer-clean-am: distclean-am maintainer-clean-generic