| #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 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); |
| exit(1); |
| } |
| } while (1); |
| |
| if (optind == argc) { |
| fprintf(stderr, "Usage: %s [-p] [-m mode] dir...\n", |
| progname); |
| exit(1); |
| } |
| |
| while (optind < argc) { |
| if (make_dir(argv[optind])) |
| ret = 255; /* seems to be what gnu mkdir does */ |
| optind++; |
| } |
| |
| return ret; |
| } |