1 //===-- runtime/file.cpp --------------------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
11 #include "flang/Runtime/magic-numbers.h"
12 #include "flang/Runtime/memory.h"
20 #include "flang/Common/windows-include.h"
26 namespace Fortran::runtime::io
{
28 void OpenFile::set_path(OwningPtr
<char> &&path
, std::size_t bytes
) {
29 path_
= std::move(path
);
33 static int openfile_mkstemp(IoErrorHandler
&handler
) {
35 const unsigned int uUnique
{0};
36 // GetTempFileNameA needs a directory name < MAX_PATH-14 characters in length.
37 // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettempfilenamea
38 char tempDirName
[MAX_PATH
- 14];
39 char tempFileName
[MAX_PATH
];
40 unsigned long nBufferLength
{sizeof(tempDirName
)};
41 nBufferLength
= ::GetTempPathA(nBufferLength
, tempDirName
);
42 if (nBufferLength
> sizeof(tempDirName
) || nBufferLength
== 0) {
45 if (::GetTempFileNameA(tempDirName
, "Fortran", uUnique
, tempFileName
) == 0) {
48 int fd
{::_open(tempFileName
, _O_CREAT
| _O_BINARY
| _O_TEMPORARY
| _O_RDWR
,
49 _S_IREAD
| _S_IWRITE
)};
51 char path
[]{"/tmp/Fortran-Scratch-XXXXXX"};
52 int fd
{::mkstemp(path
)};
55 handler
.SignalErrno();
63 void OpenFile::Open(OpenStatus status
, Fortran::common::optional
<Action
> action
,
64 Position position
, IoErrorHandler
&handler
) {
66 (status
== OpenStatus::Old
|| status
== OpenStatus::Unknown
)) {
70 if (status
== OpenStatus::Scratch
) {
72 handler
.SignalError("FILE= must not appear with STATUS='SCRATCH'");
76 action
= Action::ReadWrite
;
78 fd_
= openfile_mkstemp(handler
);
81 handler
.SignalError("FILE= is required");
86 // We emit explicit CR+LF line endings and cope with them on input
87 // for formatted files, since we can't yet always know now at OPEN
88 // time whether the file is formatted or not.
91 if (status
!= OpenStatus::Old
) {
94 if (status
== OpenStatus::New
) {
96 } else if (status
== OpenStatus::Replace
) {
100 // Try to open read/write, back off to read-only or even write-only
102 fd_
= ::open(path_
.get(), flags
| O_RDWR
, 0600);
104 action
= Action::ReadWrite
;
106 fd_
= ::open(path_
.get(), flags
| O_RDONLY
, 0600);
108 action
= Action::Read
;
110 action
= Action::Write
;
122 case Action::ReadWrite
:
126 fd_
= ::open(path_
.get(), flags
, 0600);
128 handler
.SignalErrno();
132 RUNTIME_CHECK(handler
, action
.has_value());
134 if (fd_
>= 0 && position
== Position::Append
&& !RawSeekToEnd()) {
135 handler
.SignalError(IostatOpenBadAppend
);
137 isTerminal_
= fd_
>= 0 && IsATerminal(fd_
) == 1;
138 mayRead_
= *action
!= Action::Write
;
139 mayWrite_
= *action
!= Action::Read
;
140 if (status
== OpenStatus::Old
|| status
== OpenStatus::Unknown
) {
144 if (fd_
>= 0 && ::fstat(fd_
, &buf
) == 0) {
145 mayPosition_
= S_ISREG(buf
.st_mode
);
146 knownSize_
= buf
.st_size
;
148 #else // TODO: _WIN32
155 openPosition_
= position
; // for INQUIRE(POSITION=)
158 void OpenFile::Predefine(int fd
) {
166 isTerminal_
= IsATerminal(fd_
) == 1;
169 mayPosition_
= false;
171 isWindowsTextFile_
= true;
175 void OpenFile::Close(CloseStatus status
, IoErrorHandler
&handler
) {
179 case CloseStatus::Keep
:
181 case CloseStatus::Delete
:
183 ::unlink(path_
.get());
191 std::size_t OpenFile::Read(FileOffset at
, char *buffer
, std::size_t minBytes
,
192 std::size_t maxBytes
, IoErrorHandler
&handler
) {
197 if (!Seek(at
, handler
)) {
200 minBytes
= std::min(minBytes
, maxBytes
);
202 while (got
< minBytes
) {
203 auto chunk
{::read(fd_
, buffer
+ got
, maxBytes
- got
)};
206 } else if (chunk
< 0) {
208 if (err
!= EAGAIN
&& err
!= EWOULDBLOCK
&& err
!= EINTR
) {
209 handler
.SignalError(err
);
213 SetPosition(position_
+ chunk
);
220 std::size_t OpenFile::Write(FileOffset at
, const char *buffer
,
221 std::size_t bytes
, IoErrorHandler
&handler
) {
226 if (!Seek(at
, handler
)) {
230 while (put
< bytes
) {
231 auto chunk
{::write(fd_
, buffer
+ put
, bytes
- put
)};
233 SetPosition(position_
+ chunk
);
237 if (err
!= EAGAIN
&& err
!= EWOULDBLOCK
&& err
!= EINTR
) {
238 handler
.SignalError(err
);
243 if (knownSize_
&& position_
> *knownSize_
) {
244 knownSize_
= position_
;
249 inline static int openfile_ftruncate(int fd
, OpenFile::FileOffset at
) {
251 return ::_chsize(fd
, at
);
253 return ::ftruncate(fd
, at
);
257 void OpenFile::Truncate(FileOffset at
, IoErrorHandler
&handler
) {
259 if (!knownSize_
|| *knownSize_
!= at
) {
260 if (openfile_ftruncate(fd_
, at
) != 0) {
261 handler
.SignalErrno();
267 // The operation is performed immediately; the results are saved
268 // to be claimed by a later WAIT statement.
269 // TODO: True asynchronicity
270 int OpenFile::ReadAsynchronously(
271 FileOffset at
, char *buffer
, std::size_t bytes
, IoErrorHandler
&handler
) {
274 for (std::size_t got
{0}; got
< bytes
;) {
275 #if _XOPEN_SOURCE >= 500 || _POSIX_C_SOURCE >= 200809L
276 auto chunk
{::pread(fd_
, buffer
+ got
, bytes
- got
, at
)};
278 auto chunk
{Seek(at
, handler
) ? ::read(fd_
, buffer
+ got
, bytes
- got
) : -1};
281 iostat
= FORTRAN_RUNTIME_IOSTAT_END
;
286 if (err
!= EAGAIN
&& err
!= EWOULDBLOCK
&& err
!= EINTR
) {
295 return PendingResult(handler
, iostat
);
298 // TODO: True asynchronicity
299 int OpenFile::WriteAsynchronously(FileOffset at
, const char *buffer
,
300 std::size_t bytes
, IoErrorHandler
&handler
) {
303 for (std::size_t put
{0}; put
< bytes
;) {
304 #if _XOPEN_SOURCE >= 500 || _POSIX_C_SOURCE >= 200809L
305 auto chunk
{::pwrite(fd_
, buffer
+ put
, bytes
- put
, at
)};
308 Seek(at
, handler
) ? ::write(fd_
, buffer
+ put
, bytes
- put
) : -1};
315 if (err
!= EAGAIN
&& err
!= EWOULDBLOCK
&& err
!= EINTR
) {
321 return PendingResult(handler
, iostat
);
324 void OpenFile::Wait(int id
, IoErrorHandler
&handler
) {
325 Fortran::common::optional
<int> ioStat
;
326 Pending
*prev
{nullptr};
327 for (Pending
*p
{pending_
.get()}; p
; p
= (prev
= p
)->next
.get()) {
331 prev
->next
.reset(p
->next
.release());
333 pending_
.reset(p
->next
.release());
339 handler
.SignalError(*ioStat
);
343 void OpenFile::WaitAll(IoErrorHandler
&handler
) {
347 ioStat
= pending_
->ioStat
;
348 pending_
.reset(pending_
->next
.release());
352 handler
.SignalError(ioStat
);
356 Position
OpenFile::InquirePosition() const {
357 if (openPosition_
) { // from OPEN statement
358 return *openPosition_
;
359 } else { // unit has been repositioned since opening
360 if (position_
== knownSize_
.value_or(position_
+ 1)) {
361 return Position::Append
;
362 } else if (position_
== 0 && mayPosition_
) {
363 return Position::Rewind
;
365 return Position::AsIs
; // processor-dependent & no common behavior
370 void OpenFile::CheckOpen(const Terminator
&terminator
) {
371 RUNTIME_CHECK(terminator
, fd_
>= 0);
374 bool OpenFile::Seek(FileOffset at
, IoErrorHandler
&handler
) {
375 if (at
== position_
) {
377 } else if (RawSeek(at
)) {
381 handler
.SignalError(IostatCannotReposition
);
386 bool OpenFile::RawSeek(FileOffset at
) {
387 #ifdef _LARGEFILE64_SOURCE
388 return ::lseek64(fd_
, at
, SEEK_SET
) == at
;
390 return ::lseek(fd_
, at
, SEEK_SET
) == at
;
394 bool OpenFile::RawSeekToEnd() {
395 #ifdef _LARGEFILE64_SOURCE
396 std::int64_t at
{::lseek64(fd_
, 0, SEEK_END
)};
398 std::int64_t at
{::lseek(fd_
, 0, SEEK_END
)};
408 int OpenFile::PendingResult(const Terminator
&terminator
, int iostat
) {
410 pending_
= New
<Pending
>{terminator
}(id
, iostat
, std::move(pending_
));
414 void OpenFile::CloseFd(IoErrorHandler
&handler
) {
417 // don't actually close a standard file descriptor, we might need it
419 if (::close(fd_
) != 0) {
420 handler
.SignalErrno();
427 #if !defined(RT_DEVICE_COMPILATION)
428 bool IsATerminal(int fd
) { return ::isatty(fd
); }
430 #if defined(_WIN32) && !defined(F_OK)
431 // Access flags are normally defined in unistd.h, which unavailable under
432 // Windows. Instead, define the flags as documented at
433 // https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/access-waccess
434 // On Mingw, io.h does define these same constants - so check whether they
435 // already are defined before defining these.
441 bool IsExtant(const char *path
) { return ::access(path
, F_OK
) == 0; }
442 bool MayRead(const char *path
) { return ::access(path
, R_OK
) == 0; }
443 bool MayWrite(const char *path
) { return ::access(path
, W_OK
) == 0; }
444 bool MayReadAndWrite(const char *path
) {
445 return ::access(path
, R_OK
| W_OK
) == 0;
448 std::int64_t SizeInBytes(const char *path
) {
451 if (::stat(path
, &buf
) == 0) {
454 #else // TODO: _WIN32
456 // No Fortran compiler signals an error
459 #else // defined(RT_DEVICE_COMPILATION)
460 RT_API_ATTRS
bool IsATerminal(int fd
) {
461 Terminator
{__FILE__
, __LINE__
}.Crash("%s: unsupported", RT_PRETTY_FUNCTION
);
463 RT_API_ATTRS
bool IsExtant(const char *path
) {
464 Terminator
{__FILE__
, __LINE__
}.Crash("%s: unsupported", RT_PRETTY_FUNCTION
);
466 RT_API_ATTRS
bool MayRead(const char *path
) {
467 Terminator
{__FILE__
, __LINE__
}.Crash("%s: unsupported", RT_PRETTY_FUNCTION
);
469 RT_API_ATTRS
bool MayWrite(const char *path
) {
470 Terminator
{__FILE__
, __LINE__
}.Crash("%s: unsupported", RT_PRETTY_FUNCTION
);
472 RT_API_ATTRS
bool MayReadAndWrite(const char *path
) {
473 Terminator
{__FILE__
, __LINE__
}.Crash("%s: unsupported", RT_PRETTY_FUNCTION
);
475 RT_API_ATTRS
std::int64_t SizeInBytes(const char *path
) {
476 Terminator
{__FILE__
, __LINE__
}.Crash("%s: unsupported", RT_PRETTY_FUNCTION
);
478 #endif // defined(RT_DEVICE_COMPILATION)
480 } // namespace Fortran::runtime::io