From b0e7307b3569a5dad0f2606d2736cc8317851598 Mon Sep 17 00:00:00 2001 From: Dominique Martinet 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 --- 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 +#include +#include +#include + +#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 #include #include + +#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