diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:06:04 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:06:04 +0000 |
commit | 2f0649f6fe411d7e07c8d56cf8ea56db53536da8 (patch) | |
tree | 778611fb52176dce1ad06c68e87b2cb348ca0f7b /usr/utils/mkdir.c | |
parent | Initial commit. (diff) | |
download | klibc-2f0649f6fe411d7e07c8d56cf8ea56db53536da8.tar.xz klibc-2f0649f6fe411d7e07c8d56cf8ea56db53536da8.zip |
Adding upstream version 2.0.13.upstream/2.0.13upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'usr/utils/mkdir.c')
-rw-r--r-- | usr/utils/mkdir.c | 154 |
1 files changed, 154 insertions, 0 deletions
diff --git a/usr/utils/mkdir.c b/usr/utils/mkdir.c new file mode 100644 index 0000000..af241ef --- /dev/null +++ b/usr/utils/mkdir.c @@ -0,0 +1,154 @@ +#include <sys/stat.h> +#include <sys/types.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "file_mode.h" + +static mode_t leaf_mode, subdir_mode; +static int p_flag; + +char *progname; + +static __noreturn usage(void) +{ + fprintf(stderr, "Usage: %s [-p] [-m mode] dir...\n", progname); + exit(1); +} + +static int make_one_dir(char *dir, mode_t mode) +{ + struct stat stbuf; + + if (mkdir(dir, mode) == -1) { + int err = errno; + + /* + * Ignore the error if it all of the following + * are satisfied: + * - error was EEXIST + * - -p was specified + * - stat indicates that its a directory + */ + if (p_flag && errno == EEXIST && + stat(dir, &stbuf) == 0 && S_ISDIR(stbuf.st_mode)) + return 1; + errno = err; + fprintf(stderr, "%s: ", progname); + perror(dir); + return -1; + } + return 0; +} + +static int make_dir(char *dir) +{ + int ret; + + if (p_flag) { + char *s, *p; + + /* + * Recurse each directory, trying to make it + * as we go. Should we check to see if it + * exists, and if so if it's a directory + * before calling mkdir? + */ + s = dir; + while ((p = strchr(s, '/')) != NULL) { + /* + * Ignore the leading / + */ + if (p != dir) { + *p = '\0'; + + /* + * Make the intermediary directory. POSIX + * says that these directories are created + * with umask,u+wx + */ + if (make_one_dir(dir, subdir_mode) == -1) + return -1; + + *p = '/'; + } + s = p + 1; + } + } + + /* + * Make the final target. Only complain if the + * target already exists if -p was not specified. + * This is created with the asked for mode & ~umask + */ + ret = make_one_dir(dir, leaf_mode); + if (ret == -1) + return -1; + + /* + * We might not set all the permission bits. Do that + * here (but only if we did create it.) + */ + if (ret == 0 && chmod(dir, leaf_mode) == -1) { + int err_save = errno; + + /* + * We failed, remove the directory we created + */ + rmdir(dir); + errno = err_save; + fprintf(stderr, "%s: ", progname); + perror(dir); + return -1; + } + return 0; +} + +int main(int argc, char *argv[]) +{ + int c, ret = 0; + mode_t saved_umask; + + progname = argv[0]; + + saved_umask = umask(0); + leaf_mode = (S_IRWXU | S_IRWXG | S_IRWXO) & ~saved_umask; + subdir_mode = (saved_umask ^ (S_IRWXU | S_IRWXG | S_IRWXO)) + | S_IWUSR | S_IXUSR; + + do { + c = getopt(argc, argv, "pm:"); + if (c == EOF) + break; + switch (c) { + case 'm': + leaf_mode = + parse_file_mode(optarg, leaf_mode, saved_umask); + break; + case 'p': + p_flag = 1; + break; + + case '?': + fprintf(stderr, "%s: invalid option -%c\n", + progname, optopt); + usage(); + } + } while (1); + + if (optind == argc) + usage(); + + while (optind < argc) { + if (make_dir(argv[optind])) + ret = 255; /* seems to be what gnu mkdir does */ + optind++; + } + + return ret; +} |