Skip to content

Commit 3c9b87a

Browse files
authored
Add some more operations to the filesystem library (#5968)
Specifically this adds `WriteStream` to get an LLVM-style `raw_fd_ostream` for an open file, and `Rename` corresponding to `rename` and `renameat` Unix-like system calls. Some basic testing for both is added as well. This was split out of work to switch the runtimes building to use the new filesystem library.
1 parent 41ed82e commit 3c9b87a

File tree

2 files changed

+101
-0
lines changed

2 files changed

+101
-0
lines changed

common/filesystem.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,13 @@ class Internal::FileRefBase {
323323
auto WriteFromBuffer(llvm::ArrayRef<std::byte> buffer)
324324
-> ErrorOr<llvm::ArrayRef<std::byte>, FdError>;
325325

326+
// Returns an LLVM `raw_fd_ostream` that writes to this file.
327+
//
328+
// Note that this doesn't expose any write errors here, those will surface
329+
// through the `raw_fd_ostream` API. The stream will also not close the file
330+
// which remains owned by the owning `File` object.
331+
auto WriteStream() -> llvm::raw_fd_ostream;
332+
326333
// Reads the file until EOF into the returned string.
327334
//
328335
// This method will retry any recoverable errors and work to completely read
@@ -416,6 +423,8 @@ class FileRef : public Internal::FileRefBase {
416423
auto WriteFromBuffer(llvm::ArrayRef<std::byte> buffer)
417424
-> ErrorOr<llvm::ArrayRef<std::byte>, FdError>
418425
requires Writeable;
426+
auto WriteStream() -> llvm::raw_fd_ostream
427+
requires Writeable;
419428
auto ReadToString() -> ErrorOr<std::string, FdError>
420429
requires Readable;
421430
auto WriteFromString(llvm::StringRef str) -> ErrorOr<Success, FdError>
@@ -673,6 +682,11 @@ class DirRef {
673682
CreationOptions creation_options = CreateAlways)
674683
-> ErrorOr<Success, PathError>;
675684

685+
// Moves a file from one directory to another directory.
686+
auto Rename(const std::filesystem::path& path, DirRef target_dir,
687+
const std::filesystem::path& target_path)
688+
-> ErrorOr<Success, PathError>;
689+
676690
// Changes the current working directory to this directory.
677691
auto Chdir() -> ErrorOr<Success, FdError>;
678692

@@ -1189,6 +1203,10 @@ inline auto Internal::FileRefBase::WriteFromBuffer(
11891203
}
11901204
}
11911205

1206+
inline auto Internal::FileRefBase::WriteStream() -> llvm::raw_fd_ostream {
1207+
return llvm::raw_fd_ostream(fd_, /*shouldClose=*/false);
1208+
}
1209+
11921210
inline auto Internal::FileRefBase::Close() && -> ErrorOr<Success, FdError> {
11931211
// Put the file in a moved-from state immediately as it is invalid to
11941212
// retry closing or use the file in any way even if the close fails.
@@ -1242,6 +1260,13 @@ auto FileRef<A>::WriteFromBuffer(llvm::ArrayRef<std::byte> buffer)
12421260
return FileRefBase::WriteFromBuffer(buffer);
12431261
}
12441262

1263+
template <OpenAccess A>
1264+
auto FileRef<A>::WriteStream() -> llvm::raw_fd_ostream
1265+
requires Writeable
1266+
{
1267+
return FileRefBase::WriteStream();
1268+
}
1269+
12451270
template <OpenAccess A>
12461271
auto FileRef<A>::WriteFromString(llvm::StringRef str)
12471272
-> ErrorOr<Success, FdError>
@@ -1351,6 +1376,17 @@ inline auto DirRef::OpenReadWrite(const std::filesystem::path& path,
13511376
flags);
13521377
}
13531378

1379+
inline auto DirRef::Rename(const std::filesystem::path& path, DirRef target_dir,
1380+
const std::filesystem::path& target_path)
1381+
-> ErrorOr<Success, PathError> {
1382+
if (renameat(dfd_, path.c_str(), target_dir.dfd_, target_path.c_str()) ==
1383+
-1) {
1384+
return PathError(errno, "Dir::Rename on '{0}' relative to '{1}'", path,
1385+
dfd_);
1386+
}
1387+
return Success();
1388+
}
1389+
13541390
inline auto DirRef::Chdir() -> ErrorOr<Success, FdError> {
13551391
if (fchdir(dfd_) == -1) {
13561392
return FdError(errno, "Dir::Chdir on '{0}'", dfd_);

common/filesystem_test.cpp

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,5 +327,70 @@ TEST_F(FilesystemTest, Chdir) {
327327
(*std::move(f)).Close().Check();
328328
}
329329

330+
TEST_F(FilesystemTest, WriteStream) {
331+
std::string content_str = "0123456789";
332+
auto write = dir_.OpenWriteOnly("test", CreationOptions::CreateNew);
333+
ASSERT_THAT(write, IsSuccess(_));
334+
{
335+
llvm::raw_fd_ostream os = write->WriteStream();
336+
os << content_str;
337+
EXPECT_FALSE(os.has_error()) << os.error();
338+
}
339+
(*std::move(write)).Close().Check();
340+
341+
EXPECT_THAT(dir_.ReadFileToString("test"), IsSuccess(Eq(content_str)));
342+
}
343+
344+
TEST_F(FilesystemTest, Rename) {
345+
// Rename a file within a directory.
346+
ASSERT_THAT(dir_.WriteFileFromString("file1", "content1"), IsSuccess(_));
347+
EXPECT_THAT(dir_.Rename("file1", dir_, "file2"), IsSuccess(_));
348+
EXPECT_THAT(dir_.ReadFileToString("file2"), IsSuccess(Eq("content1")));
349+
auto read_missing = dir_.ReadFileToString("file1");
350+
EXPECT_FALSE(read_missing.ok());
351+
EXPECT_TRUE(read_missing.error().no_entity());
352+
353+
// Rename a file between two directories.
354+
auto d1 = *dir_.CreateDirectories("subdir1");
355+
EXPECT_THAT(dir_.Rename("file2", d1, "file1"), IsSuccess(_));
356+
EXPECT_THAT(d1.ReadFileToString("file1"), IsSuccess(Eq("content1")));
357+
auto d2 = *dir_.CreateDirectories("subdir2");
358+
EXPECT_THAT(d1.Rename("file1", d2, "file1"), IsSuccess(_));
359+
EXPECT_THAT(d2.ReadFileToString("file1"), IsSuccess(Eq("content1")));
360+
// Close the first directory.
361+
d1 = Filesystem::Dir();
362+
EXPECT_THAT(dir_.Rmdir("subdir1"), IsSuccess(_))
363+
<< "Directory should have bene empty!";
364+
365+
// Rename directories.
366+
ASSERT_THAT(dir_.ReadFileToString(std::filesystem::path("subdir2") / "file1"),
367+
IsSuccess(Eq("content1")));
368+
EXPECT_THAT(dir_.Rename("subdir2", dir_, "subdir1"), IsSuccess(_));
369+
EXPECT_THAT(dir_.ReadFileToString(std::filesystem::path("subdir1") / "file1"),
370+
IsSuccess(Eq("content1")));
371+
372+
// The open directory `d2` should survive the rename and point at the same
373+
// directory.
374+
EXPECT_THAT(d2.ReadFileToString("file1"), IsSuccess(Eq("content1")));
375+
EXPECT_THAT(d2.WriteFileFromString("file2", "content2"), IsSuccess(_));
376+
EXPECT_THAT(dir_.ReadFileToString(std::filesystem::path("subdir1") / "file2"),
377+
IsSuccess(Eq("content2")));
378+
379+
// Rename over an existing file.
380+
EXPECT_THAT(d2.Rename("file2", d2, "file1"), IsSuccess(_));
381+
EXPECT_THAT(d2.ReadFileToString("file1"), IsSuccess(Eq("content2")));
382+
383+
// Test error calls as well.
384+
auto result = dir_.Rename("missing1", dir_, "missing2");
385+
EXPECT_TRUE(result.error().no_entity()) << result.error();
386+
result = d2.Rename("file1", dir_,
387+
std::filesystem::path("missing_subdir") / "file2");
388+
EXPECT_TRUE(result.error().no_entity()) << result.error();
389+
// Note that `d2` was renamed `subdir1` above, which is why this creates
390+
// infinite subdirectories.
391+
result = dir_.Rename("subdir1", d2, "infinite_subdirs");
392+
EXPECT_THAT(result.error().unix_errnum(), EINVAL) << result.error();
393+
}
394+
330395
} // namespace
331396
} // namespace Carbon::Filesystem

0 commit comments

Comments
 (0)