Skip to content

Commit 95b2e00

Browse files
authored
Validate labels and extensions for temporary entries (#103)
Constructors now throw `std::invalid_argument` when the `label` or `extension` arguments have invalid values
1 parent a14e574 commit 95b2e00

File tree

8 files changed

+90
-6
lines changed

8 files changed

+90
-6
lines changed

include/tmp/directory

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ public:
4343
///
4444
/// @param label A label to attach to the temporary directory path
4545
/// @throws std::filesystem::filesystem_error if cannot create a directory
46+
/// @throws std::invalid_argument if the label is ill-formatted
4647
explicit directory(std::string_view label = "");
4748

4849
/// Creates a unique temporary copy recursively from the given path
@@ -51,6 +52,7 @@ public:
5152
/// @param label A label to attach to the temporary directory path
5253
/// @returns The new temporary directory
5354
/// @throws std::filesystem::filesystem_error if @p path is not a directory
55+
/// @throws std::invalid_argument if the label is ill-formatted
5456
static directory copy(const std::filesystem::path& path,
5557
std::string_view label = "");
5658

include/tmp/file

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ public:
5656
/// @param label A label to attach to the temporary file path
5757
/// @param extension An extension of the temporary file path
5858
/// @throws std::filesystem::filesystem_error if cannot create a file
59+
/// @throws std::invalid_argument if arguments are ill-formatted
5960
explicit file(std::string_view label = "", std::string_view extension = "");
6061

6162
/// Creates a unique temporary file and opens it for reading and writing
@@ -64,6 +65,7 @@ public:
6465
/// @param label A label to attach to the temporary file path
6566
/// @param extension An extension of the temporary file path
6667
/// @throws std::filesystem::filesystem_error if cannot create a file
68+
/// @throws std::invalid_argument if arguments are ill-formatted
6769
static file text(std::string_view label = "",
6870
std::string_view extension = "");
6971

@@ -73,7 +75,8 @@ public:
7375
/// @param label A label to attach to the temporary file path
7476
/// @param extension An extension of the temporary file path
7577
/// @returns The new temporary file
76-
/// @throws std::filesystem::filesystem_error if @p path is not a file
78+
/// @throws std::filesystem::filesystem_error if path is not a file
79+
/// @throws std::invalid_argument if arguments are ill-formatted
7780
static file copy(const std::filesystem::path& path,
7881
std::string_view label = "",
7982
std::string_view extension = "");
@@ -115,7 +118,8 @@ private:
115118
/// @param label A label to attach to the temporary file path
116119
/// @param extension An extension of the temporary file path
117120
/// @param binary Whether the managed file is opened in binary write mode
118-
/// @throws fs::filesystem_error if cannot create a file
121+
/// @throws std::filesystem::filesystem_error if cannot create a file
122+
/// @throws std::invalid_argument if arguments are ill-formatted
119123
file(std::string_view label, std::string_view extension, bool binary);
120124

121125
/// Creates a unique temporary file

src/directory.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ namespace {
2020
/// temporary directory, and returns its path
2121
/// @param label A label to attach to the temporary directory path
2222
/// @returns A path to the created temporary directory
23-
/// @throws fs::filesystem_error if cannot create the temporary directory
23+
/// @throws fs::filesystem_error if cannot create a temporary directory
24+
/// @throws std::invalid_argument if the label is ill-formatted
2425
fs::path create_directory(std::string_view label) {
2526
fs::path::string_type path = make_pattern(label, "");
2627

src/file.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ static_assert(std::is_same_v<HANDLE, void*>);
3535
/// @param label A label to attach to the temporary file path
3636
/// @param extension An extension of the temporary file path
3737
/// @returns A path to the created temporary file and a handle to it
38-
/// @throws fs::filesystem_error if cannot create the temporary file
38+
/// @throws fs::filesystem_error if cannot create a temporary file
39+
/// @throws std::invalid_argument if the label or extension is ill-formatted
3940
std::pair<fs::path, file::native_handle_type>
4041
create_file(std::string_view label, std::string_view extension) {
4142
fs::path::string_type path = make_pattern(label, extension);

src/utils.cpp

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,50 @@
1111

1212
namespace tmp {
1313

14+
namespace {
15+
16+
/// Checks that the given label is valid to attach to a temporary entry path
17+
/// @param label The label to check validity for
18+
/// @throws std::invalid_argument if the label cannot be attached to a path
19+
void validate_label(const fs::path& label) {
20+
if (label.empty()) {
21+
return;
22+
}
23+
24+
if (++label.begin() != label.end() || label.is_absolute() ||
25+
label.has_root_path() || label.filename() == "." ||
26+
label.filename() == "..") {
27+
throw std::invalid_argument(
28+
"Cannot create a temporary entry: label must be empty or a valid "
29+
"single-segmented relative pathname");
30+
}
31+
}
32+
33+
/// Checks that the given extension is valid to be an extension of a file path
34+
/// @param extension The extension to check validity for
35+
/// @throws std::invalid_argument if the extension cannot be used in a file path
36+
void validate_extension(std::string_view extension) {
37+
if (extension.empty()) {
38+
return;
39+
}
40+
41+
fs::path path = extension;
42+
if (++path.begin() != path.end()) {
43+
throw std::invalid_argument(
44+
"Cannot create a temporary file: extension must be empty or a valid "
45+
"single-segmented pathname");
46+
}
47+
}
48+
} // namespace
49+
1450
bool create_parent(const fs::path& path, std::error_code& ec) {
1551
return fs::create_directories(path.parent_path(), ec);
1652
}
1753

1854
fs::path make_pattern(std::string_view label, std::string_view extension) {
55+
validate_label(label);
56+
validate_extension(extension);
57+
1958
#ifdef _WIN32
2059
constexpr static std::size_t CHARS_IN_GUID = 39;
2160
GUID guid;
@@ -32,8 +71,8 @@ fs::path make_pattern(std::string_view label, std::string_view extension) {
3271
#endif
3372

3473
fs::path pattern = fs::temp_directory_path() / label / name;
35-
3674
pattern += extension;
75+
3776
return pattern;
3877
}
3978
} // namespace tmp

src/utils.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ bool create_parent(const fs::path& path, std::error_code& ec);
2323
/// @param label A label to attach to the path pattern
2424
/// @param extension An extension of the temporary file path
2525
/// @returns A path pattern for the unique temporary path
26-
/// @throws std::bad_alloc if memory allocation fails
26+
/// @throws std::invalid_argument if the label or extension is ill-formatted
27+
/// @throws std::bad_alloc if memory allocation fails
2728
fs::path make_pattern(std::string_view label, std::string_view extension);
2829
} // namespace tmp

tests/directory.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,20 @@ TEST(directory, create_multiple) {
4949
EXPECT_FALSE(fs::equivalent(fst, snd));
5050
}
5151

52+
/// Tests error handling with invalid labels
53+
TEST(directory, create_invalid_label) {
54+
EXPECT_THROW(directory("multi/segment"), std::invalid_argument);
55+
EXPECT_THROW(directory("/root"), std::invalid_argument);
56+
EXPECT_THROW(directory(".."), std::invalid_argument);
57+
EXPECT_THROW(directory("."), std::invalid_argument);
58+
59+
fs::path root = fs::temp_directory_path().root_name();
60+
if (!root.empty()) {
61+
EXPECT_THROW(directory(root.string() + "relative"), std::invalid_argument);
62+
EXPECT_THROW(directory(root.string() + "/root"), std::invalid_argument);
63+
}
64+
}
65+
5266
/// Tests creation of a temporary copy of a directory
5367
TEST(directory, copy_directory) {
5468
directory tmpdir = directory();

tests/file.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,28 @@ TEST(file, create_multiple) {
8484
EXPECT_FALSE(fs::equivalent(fst, snd));
8585
}
8686

87+
/// Tests error handling with invalid labels
88+
TEST(file, create_invalid_label) {
89+
EXPECT_THROW(file("multi/segment"), std::invalid_argument);
90+
EXPECT_THROW(file("/root"), std::invalid_argument);
91+
EXPECT_THROW(file(".."), std::invalid_argument);
92+
EXPECT_THROW(file("."), std::invalid_argument);
93+
94+
fs::path root = fs::temp_directory_path().root_name();
95+
if (!root.empty()) {
96+
EXPECT_THROW(file(root.string() + "relative"), std::invalid_argument);
97+
EXPECT_THROW(file(root.string() + "/root"), std::invalid_argument);
98+
}
99+
}
100+
101+
/// Tests error handling with invalid extensions
102+
TEST(file, create_invalid_extension) {
103+
EXPECT_THROW(file("", "multi/segment"), std::invalid_argument);
104+
EXPECT_THROW(file("", "/root"), std::invalid_argument);
105+
EXPECT_THROW(file("", "/.."), std::invalid_argument);
106+
EXPECT_THROW(file("", "/."), std::invalid_argument);
107+
}
108+
87109
/// Tests creation of a temporary copy of a file
88110
TEST(file, copy_file) {
89111
file tmpfile = file();

0 commit comments

Comments
 (0)