Skip to content

Commit 46dc91e

Browse files
authored
[SystemZ][z/OS] Add new openFileForReadBinary function, and pass IsText parameter to getBufferForFile (#111723)
This patch adds an IsText parameter to the following getBufferForFile, getBufferForFileImpl. We introduce a new virtual function openFileForReadBinary which defaults to openFileForRead except in RealFileSystem which uses the OF_None flag instead of OF_Text. The default is set to OF_Text instead of OF_None, this change in value does not affect any other platforms other than z/OS. Setting this parameter correctly is required to open files on z/OS in the correct encoding. The IsText parameter is based on the context of where we open files, for example, in the ASTReader, HeaderMap requires that files always be opened in binary even though they might be tagged as text.
1 parent c47df3e commit 46dc91e

File tree

10 files changed

+205
-53
lines changed

10 files changed

+205
-53
lines changed

clang/include/clang/Basic/FileManager.h

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,8 @@ class FileManager : public RefCountedBase<FileManager> {
124124
std::unique_ptr<FileSystemStatCache> StatCache;
125125

126126
std::error_code getStatValue(StringRef Path, llvm::vfs::Status &Status,
127-
bool isFile,
128-
std::unique_ptr<llvm::vfs::File> *F);
127+
bool isFile, std::unique_ptr<llvm::vfs::File> *F,
128+
bool IsText = true);
129129

130130
/// Add all ancestors of the given path (pointing to either a file
131131
/// or a directory) as virtual directories.
@@ -230,7 +230,8 @@ class FileManager : public RefCountedBase<FileManager> {
230230
/// the failure to find this file.
231231
llvm::Expected<FileEntryRef> getFileRef(StringRef Filename,
232232
bool OpenFile = false,
233-
bool CacheFailure = true);
233+
bool CacheFailure = true,
234+
bool IsText = true);
234235

235236
/// Get the FileEntryRef for stdin, returning an error if stdin cannot be
236237
/// read.
@@ -290,23 +291,28 @@ class FileManager : public RefCountedBase<FileManager> {
290291

291292
/// Open the specified file as a MemoryBuffer, returning a new
292293
/// MemoryBuffer if successful, otherwise returning null.
294+
/// The IsText parameter controls whether the file should be opened as a text
295+
/// or binary file, and should be set to false if the file contents should be
296+
/// treated as binary.
293297
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
294298
getBufferForFile(FileEntryRef Entry, bool isVolatile = false,
295299
bool RequiresNullTerminator = true,
296-
std::optional<int64_t> MaybeLimit = std::nullopt);
300+
std::optional<int64_t> MaybeLimit = std::nullopt,
301+
bool IsText = true);
297302
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
298303
getBufferForFile(StringRef Filename, bool isVolatile = false,
299304
bool RequiresNullTerminator = true,
300-
std::optional<int64_t> MaybeLimit = std::nullopt) const {
305+
std::optional<int64_t> MaybeLimit = std::nullopt,
306+
bool IsText = true) const {
301307
return getBufferForFileImpl(Filename,
302308
/*FileSize=*/MaybeLimit.value_or(-1),
303-
isVolatile, RequiresNullTerminator);
309+
isVolatile, RequiresNullTerminator, IsText);
304310
}
305311

306312
private:
307313
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
308314
getBufferForFileImpl(StringRef Filename, int64_t FileSize, bool isVolatile,
309-
bool RequiresNullTerminator) const;
315+
bool RequiresNullTerminator, bool IsText) const;
310316

311317
DirectoryEntry *&getRealDirEntry(const llvm::vfs::Status &Status);
312318

clang/include/clang/Basic/FileSystemStatCache.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,10 @@ class FileSystemStatCache {
4848
/// success for directories (not files). On a successful file lookup, the
4949
/// implementation can optionally fill in \p F with a valid \p File object and
5050
/// the client guarantees that it will close it.
51-
static std::error_code
52-
get(StringRef Path, llvm::vfs::Status &Status, bool isFile,
53-
std::unique_ptr<llvm::vfs::File> *F,
54-
FileSystemStatCache *Cache, llvm::vfs::FileSystem &FS);
51+
static std::error_code get(StringRef Path, llvm::vfs::Status &Status,
52+
bool isFile, std::unique_ptr<llvm::vfs::File> *F,
53+
FileSystemStatCache *Cache,
54+
llvm::vfs::FileSystem &FS, bool IsText = true);
5555

5656
protected:
5757
// FIXME: The pointer here is a non-owning/optional reference to the

clang/lib/Basic/FileManager.cpp

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -212,8 +212,10 @@ FileManager::getFile(StringRef Filename, bool openFile, bool CacheFailure) {
212212
return llvm::errorToErrorCode(Result.takeError());
213213
}
214214

215-
llvm::Expected<FileEntryRef>
216-
FileManager::getFileRef(StringRef Filename, bool openFile, bool CacheFailure) {
215+
llvm::Expected<FileEntryRef> FileManager::getFileRef(StringRef Filename,
216+
bool openFile,
217+
bool CacheFailure,
218+
bool IsText) {
217219
++NumFileLookups;
218220

219221
// See if there is already an entry in the map.
@@ -259,7 +261,7 @@ FileManager::getFileRef(StringRef Filename, bool openFile, bool CacheFailure) {
259261
std::unique_ptr<llvm::vfs::File> F;
260262
llvm::vfs::Status Status;
261263
auto statError = getStatValue(InterndFileName, Status, true,
262-
openFile ? &F : nullptr);
264+
openFile ? &F : nullptr, IsText);
263265
if (statError) {
264266
// There's no real file at the given path.
265267
if (CacheFailure)
@@ -531,7 +533,7 @@ void FileManager::fillRealPathName(FileEntry *UFE, llvm::StringRef FileName) {
531533
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
532534
FileManager::getBufferForFile(FileEntryRef FE, bool isVolatile,
533535
bool RequiresNullTerminator,
534-
std::optional<int64_t> MaybeLimit) {
536+
std::optional<int64_t> MaybeLimit, bool IsText) {
535537
const FileEntry *Entry = &FE.getFileEntry();
536538
// If the content is living on the file entry, return a reference to it.
537539
if (Entry->Content)
@@ -558,42 +560,44 @@ FileManager::getBufferForFile(FileEntryRef FE, bool isVolatile,
558560

559561
// Otherwise, open the file.
560562
return getBufferForFileImpl(Filename, FileSize, isVolatile,
561-
RequiresNullTerminator);
563+
RequiresNullTerminator, IsText);
562564
}
563565

564566
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
565567
FileManager::getBufferForFileImpl(StringRef Filename, int64_t FileSize,
566-
bool isVolatile,
567-
bool RequiresNullTerminator) const {
568+
bool isVolatile, bool RequiresNullTerminator,
569+
bool IsText) const {
568570
if (FileSystemOpts.WorkingDir.empty())
569571
return FS->getBufferForFile(Filename, FileSize, RequiresNullTerminator,
570-
isVolatile);
572+
isVolatile, IsText);
571573

572574
SmallString<128> FilePath(Filename);
573575
FixupRelativePath(FilePath);
574576
return FS->getBufferForFile(FilePath, FileSize, RequiresNullTerminator,
575-
isVolatile);
577+
isVolatile, IsText);
576578
}
577579

578580
/// getStatValue - Get the 'stat' information for the specified path,
579581
/// using the cache to accelerate it if possible. This returns true
580582
/// if the path points to a virtual file or does not exist, or returns
581583
/// false if it's an existent real file. If FileDescriptor is NULL,
582584
/// do directory look-up instead of file look-up.
583-
std::error_code
584-
FileManager::getStatValue(StringRef Path, llvm::vfs::Status &Status,
585-
bool isFile, std::unique_ptr<llvm::vfs::File> *F) {
585+
std::error_code FileManager::getStatValue(StringRef Path,
586+
llvm::vfs::Status &Status,
587+
bool isFile,
588+
std::unique_ptr<llvm::vfs::File> *F,
589+
bool IsText) {
586590
// FIXME: FileSystemOpts shouldn't be passed in here, all paths should be
587591
// absolute!
588592
if (FileSystemOpts.WorkingDir.empty())
589-
return FileSystemStatCache::get(Path, Status, isFile, F,
590-
StatCache.get(), *FS);
593+
return FileSystemStatCache::get(Path, Status, isFile, F, StatCache.get(),
594+
*FS, IsText);
591595

592596
SmallString<128> FilePath(Path);
593597
FixupRelativePath(FilePath);
594598

595599
return FileSystemStatCache::get(FilePath.c_str(), Status, isFile, F,
596-
StatCache.get(), *FS);
600+
StatCache.get(), *FS, IsText);
597601
}
598602

599603
std::error_code

clang/lib/Basic/FileSystemStatCache.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,12 @@ void FileSystemStatCache::anchor() {}
3030
/// success for directories (not files). On a successful file lookup, the
3131
/// implementation can optionally fill in FileDescriptor with a valid
3232
/// descriptor and the client guarantees that it will close it.
33-
std::error_code
34-
FileSystemStatCache::get(StringRef Path, llvm::vfs::Status &Status,
35-
bool isFile, std::unique_ptr<llvm::vfs::File> *F,
36-
FileSystemStatCache *Cache,
37-
llvm::vfs::FileSystem &FS) {
33+
std::error_code FileSystemStatCache::get(StringRef Path,
34+
llvm::vfs::Status &Status, bool isFile,
35+
std::unique_ptr<llvm::vfs::File> *F,
36+
FileSystemStatCache *Cache,
37+
llvm::vfs::FileSystem &FS,
38+
bool IsText) {
3839
bool isForDir = !isFile;
3940
std::error_code RetCode;
4041

@@ -58,7 +59,8 @@ FileSystemStatCache::get(StringRef Path, llvm::vfs::Status &Status,
5859
//
5960
// Because of this, check to see if the file exists with 'open'. If the
6061
// open succeeds, use fstat to get the stat info.
61-
auto OwnedFile = FS.openFileForRead(Path);
62+
auto OwnedFile =
63+
IsText ? FS.openFileForRead(Path) : FS.openFileForReadBinary(Path);
6264

6365
if (!OwnedFile) {
6466
// If the open fails, our "stat" fails.

clang/lib/Lex/HeaderMap.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,10 @@ std::unique_ptr<HeaderMap> HeaderMap::Create(FileEntryRef FE, FileManager &FM) {
5454
unsigned FileSize = FE.getSize();
5555
if (FileSize <= sizeof(HMapHeader)) return nullptr;
5656

57-
auto FileBuffer = FM.getBufferForFile(FE);
57+
auto FileBuffer =
58+
FM.getBufferForFile(FE, /*IsVolatile=*/false,
59+
/*RequiresNullTerminator=*/true,
60+
/*MaybeList=*/std::nullopt, /*IsText=*/false);
5861
if (!FileBuffer || !*FileBuffer)
5962
return nullptr;
6063
bool NeedsByteSwap;

clang/lib/Lex/PPDirectives.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1080,8 +1080,8 @@ Preprocessor::LookupEmbedFile(StringRef Filename, bool isAngled, bool OpenFile,
10801080
FileManager &FM = this->getFileManager();
10811081
if (llvm::sys::path::is_absolute(Filename)) {
10821082
// lookup path or immediately fail
1083-
llvm::Expected<FileEntryRef> ShouldBeEntry =
1084-
FM.getFileRef(Filename, OpenFile);
1083+
llvm::Expected<FileEntryRef> ShouldBeEntry = FM.getFileRef(
1084+
Filename, OpenFile, /*CacheFailure=*/true, /*IsText=*/false);
10851085
return llvm::expectedToOptional(std::move(ShouldBeEntry));
10861086
}
10871087

@@ -1107,8 +1107,8 @@ Preprocessor::LookupEmbedFile(StringRef Filename, bool isAngled, bool OpenFile,
11071107
StringRef FullFileDir = LookupFromFile->tryGetRealPathName();
11081108
if (!FullFileDir.empty()) {
11091109
SeparateComponents(LookupPath, FullFileDir, Filename, true);
1110-
llvm::Expected<FileEntryRef> ShouldBeEntry =
1111-
FM.getFileRef(LookupPath, OpenFile);
1110+
llvm::Expected<FileEntryRef> ShouldBeEntry = FM.getFileRef(
1111+
LookupPath, OpenFile, /*CacheFailure=*/true, /*IsText=*/false);
11121112
if (ShouldBeEntry)
11131113
return llvm::expectedToOptional(std::move(ShouldBeEntry));
11141114
llvm::consumeError(ShouldBeEntry.takeError());
@@ -1123,8 +1123,8 @@ Preprocessor::LookupEmbedFile(StringRef Filename, bool isAngled, bool OpenFile,
11231123
StringRef WorkingDir = WorkingDirEntry.getName();
11241124
if (!WorkingDir.empty()) {
11251125
SeparateComponents(LookupPath, WorkingDir, Filename, false);
1126-
llvm::Expected<FileEntryRef> ShouldBeEntry =
1127-
FM.getFileRef(LookupPath, OpenFile);
1126+
llvm::Expected<FileEntryRef> ShouldBeEntry = FM.getFileRef(
1127+
LookupPath, OpenFile, /*CacheFailure=*/true, /*IsText=*/false);
11281128
if (ShouldBeEntry)
11291129
return llvm::expectedToOptional(std::move(ShouldBeEntry));
11301130
llvm::consumeError(ShouldBeEntry.takeError());
@@ -1135,8 +1135,8 @@ Preprocessor::LookupEmbedFile(StringRef Filename, bool isAngled, bool OpenFile,
11351135
for (const auto &Entry : PPOpts->EmbedEntries) {
11361136
LookupPath.clear();
11371137
SeparateComponents(LookupPath, Entry, Filename, false);
1138-
llvm::Expected<FileEntryRef> ShouldBeEntry =
1139-
FM.getFileRef(LookupPath, OpenFile);
1138+
llvm::Expected<FileEntryRef> ShouldBeEntry = FM.getFileRef(
1139+
LookupPath, OpenFile, /*CacheFailure=*/true, /*IsText=*/false);
11401140
if (ShouldBeEntry)
11411141
return llvm::expectedToOptional(std::move(ShouldBeEntry));
11421142
llvm::consumeError(ShouldBeEntry.takeError());

clang/lib/Serialization/ASTReader.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5334,7 +5334,9 @@ std::string ASTReader::getOriginalSourceFile(
53345334
const PCHContainerReader &PCHContainerRdr, DiagnosticsEngine &Diags) {
53355335
// Open the AST file.
53365336
auto Buffer = FileMgr.getBufferForFile(ASTFileName, /*IsVolatile=*/false,
5337-
/*RequiresNullTerminator=*/false);
5337+
/*RequiresNullTerminator=*/false,
5338+
/*MaybeLimit=*/std::nullopt,
5339+
/*IsText=*/false);
53385340
if (!Buffer) {
53395341
Diags.Report(diag::err_fe_unable_to_read_pch_file)
53405342
<< ASTFileName << Buffer.getError().message();

clang/test/Preprocessor/embed_zos.c

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// RUN: rm -rf %t
2+
// RUN: mkdir -p %t/media && cp %S/Inputs/media/art.txt %t/media/
3+
// RUN: chtag -r %t/media/art.txt
4+
// RUN: %clang_cc1 -std=c23 %s -fsyntax-only --embed-dir=%t -verify
5+
// expected-no-diagnostics
6+
7+
// REQUIRES: shell, system-zos
8+
9+
const char data[] = {
10+
#embed <media/art.txt>
11+
};
12+
const char data2[] = {
13+
#embed <media/art.txt>
14+
, 0
15+
};
16+
const char data3[] = {
17+
#embed <media/art.txt> suffix(, 0)
18+
};
19+
const char data4[] = {
20+
#embed <media/art.txt> suffix(,)
21+
0
22+
};
23+
static_assert(sizeof(data) == 274);
24+
static_assert(' ' == data[0]);
25+
static_assert('_' == data[11]);
26+
static_assert('\n' == data[273]);
27+
static_assert(sizeof(data2) == 275);
28+
static_assert(' ' == data2[0]);
29+
static_assert('_' == data2[11]);
30+
static_assert('\n' == data2[273]);
31+
static_assert('\0' == data2[274]);
32+
static_assert(sizeof(data3) == 275);
33+
static_assert(' ' == data3[0]);
34+
static_assert('_' == data3[11]);
35+
static_assert('\n' == data3[273]);
36+
static_assert('\0' == data3[274]);
37+
static_assert(sizeof(data4) == 275);
38+
static_assert(' ' == data4[0]);
39+
static_assert('_' == data4[11]);
40+
static_assert('\n' == data4[273]);
41+
static_assert('\0' == data4[274]);
42+
43+
const signed char data5[] = {
44+
#embed <media/art.txt>
45+
};
46+
const signed char data6[] = {
47+
#embed <media/art.txt>
48+
, 0
49+
};
50+
const signed char data7[] = {
51+
#embed <media/art.txt> suffix(, 0)
52+
};
53+
const signed char data8[] = {
54+
#embed <media/art.txt> suffix(,)
55+
0
56+
};
57+
static_assert(sizeof(data5) == 274);
58+
static_assert(' ' == data5[0]);
59+
static_assert('_' == data5[11]);
60+
static_assert('\n' == data5[273]);
61+
static_assert(sizeof(data6) == 275);
62+
static_assert(' ' == data6[0]);
63+
static_assert('_' == data6[11]);
64+
static_assert('\n' == data6[273]);
65+
static_assert('\0' == data6[274]);
66+
static_assert(sizeof(data7) == 275);
67+
static_assert(' ' == data7[0]);
68+
static_assert('_' == data7[11]);
69+
static_assert('\n' == data7[273]);
70+
static_assert('\0' == data7[274]);
71+
static_assert(sizeof(data8) == 275);
72+
static_assert(' ' == data8[0]);
73+
static_assert('_' == data8[11]);
74+
static_assert('\n' == data8[273]);
75+
static_assert('\0' == data8[274]);
76+
77+
const unsigned char data9[] = {
78+
#embed <media/art.txt>
79+
};
80+
const unsigned char data10[] = {
81+
0,
82+
#embed <media/art.txt>
83+
};
84+
const unsigned char data11[] = {
85+
#embed <media/art.txt> prefix(0,)
86+
};
87+
const unsigned char data12[] = {
88+
0
89+
#embed <media/art.txt> prefix(,)
90+
};
91+
static_assert(sizeof(data9) == 274);
92+
static_assert(' ' == data9[0]);
93+
static_assert('_' == data9[11]);
94+
static_assert('\n' == data9[273]);
95+
static_assert(sizeof(data10) == 275);
96+
static_assert(' ' == data10[1]);
97+
static_assert('_' == data10[12]);
98+
static_assert('\n' == data10[274]);
99+
static_assert('\0' == data10[0]);
100+
static_assert(sizeof(data11) == 275);
101+
static_assert(' ' == data11[1]);
102+
static_assert('_' == data11[12]);
103+
static_assert('\n' == data11[274]);
104+
static_assert('\0' == data11[0]);
105+
static_assert(sizeof(data12) == 275);
106+
static_assert(' ' == data12[1]);
107+
static_assert('_' == data12[12]);
108+
static_assert('\n' == data12[274]);
109+
static_assert('\0' == data12[0]);

llvm/include/llvm/Support/VirtualFileSystem.h

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,15 +271,28 @@ class FileSystem : public llvm::ThreadSafeRefCountedBase<FileSystem>,
271271
/// Get the status of the entry at \p Path, if one exists.
272272
virtual llvm::ErrorOr<Status> status(const Twine &Path) = 0;
273273

274-
/// Get a \p File object for the file at \p Path, if one exists.
274+
/// Get a \p File object for the text file at \p Path, if one exists.
275275
virtual llvm::ErrorOr<std::unique_ptr<File>>
276276
openFileForRead(const Twine &Path) = 0;
277277

278+
/// Get a \p File object for the binary file at \p Path, if one exists.
279+
/// Some non-ascii based file systems perform encoding conversions
280+
/// when reading as a text file, and this function should be used if
281+
/// a file's bytes should be read as-is. On most filesystems, this
282+
/// is the same behaviour as openFileForRead.
283+
virtual llvm::ErrorOr<std::unique_ptr<File>>
284+
openFileForReadBinary(const Twine &Path) {
285+
return openFileForRead(Path);
286+
}
287+
278288
/// This is a convenience method that opens a file, gets its content and then
279289
/// closes the file.
290+
/// The IsText parameter is used to distinguish whether the file should be
291+
/// opened as a binary or text file.
280292
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
281293
getBufferForFile(const Twine &Name, int64_t FileSize = -1,
282-
bool RequiresNullTerminator = true, bool IsVolatile = false);
294+
bool RequiresNullTerminator = true, bool IsVolatile = false,
295+
bool IsText = true);
283296

284297
/// Get a directory_iterator for \p Dir.
285298
/// \note The 'end' iterator is directory_iterator().

0 commit comments

Comments
 (0)