Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * Copyright (c) 2020-2021, Linus Groh <linusg@serenityos.org>
4 * Copyright (c) 2021, Xavier Defrang <xavier.defrang@gmail.com>
5 *
6 * SPDX-License-Identifier: BSD-2-Clause
7 */
8
9#include <AK/LexicalPath.h>
10#include <AK/StringBuilder.h>
11#include <LibCore/ArgsParser.h>
12#include <LibCore/FilePermissionsMask.h>
13#include <LibCore/System.h>
14#include <LibMain/Main.h>
15#include <errno.h>
16#include <sys/stat.h>
17#include <unistd.h>
18
19ErrorOr<int> serenity_main(Main::Arguments arguments)
20{
21 TRY(Core::System::pledge("stdio cpath rpath"));
22
23 bool create_parents = false;
24 DeprecatedString mode_string;
25 Vector<DeprecatedString> directories;
26
27 Core::ArgsParser args_parser;
28 args_parser.add_option(create_parents, "Create parent directories if they don't exist", "parents", 'p');
29 args_parser.add_option(mode_string, "Set new directory permissions", "mode", 'm', "mode");
30 args_parser.add_positional_argument(directories, "Directories to create", "directories");
31 args_parser.parse(arguments);
32
33 mode_t const default_mode = 0755;
34 mode_t const mask_reference_mode = 0777;
35
36 Core::FilePermissionsMask mask;
37
38 if (mode_string.is_empty()) {
39 mask.assign_permissions(default_mode);
40 } else {
41 mask = TRY(Core::FilePermissionsMask::parse(mode_string));
42 }
43
44 bool has_errors = false;
45
46 for (auto& directory : directories) {
47 LexicalPath lexical_path(directory);
48 if (!create_parents) {
49 if (mkdir(lexical_path.string().characters(), mask.apply(mask_reference_mode)) < 0) {
50 perror("mkdir");
51 has_errors = true;
52 }
53 continue;
54 }
55 StringBuilder path_builder;
56 if (lexical_path.is_absolute())
57 path_builder.append('/');
58
59 auto& parts = lexical_path.parts_view();
60 size_t num_parts = parts.size();
61
62 for (size_t idx = 0; idx < num_parts; ++idx) {
63 auto& part = parts[idx];
64
65 path_builder.append(part);
66 auto path = path_builder.to_deprecated_string();
67
68 struct stat st;
69 if (stat(path.characters(), &st) < 0) {
70 if (errno != ENOENT) {
71 perror("stat");
72 has_errors = true;
73 break;
74 }
75
76 bool is_final = (idx == (num_parts - 1));
77 mode_t mode = is_final ? mask.apply(mask_reference_mode) : default_mode;
78
79 if (mkdir(path.characters(), mode) < 0) {
80 perror("mkdir");
81 has_errors = true;
82 break;
83 }
84 } else {
85 if (!S_ISDIR(st.st_mode)) {
86 warnln("mkdir: cannot create directory '{}': not a directory", path);
87 has_errors = true;
88 break;
89 }
90 }
91 path_builder.append('/');
92 }
93 }
94 return has_errors ? 1 : 0;
95}