llvm-project

Форк
0
480 строк · 12.4 Кб
1
//===-- runtime/file.cpp --------------------------------------------------===//
2
//
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
6
//
7
//===----------------------------------------------------------------------===//
8

9
#include "file.h"
10
#include "tools.h"
11
#include "flang/Runtime/magic-numbers.h"
12
#include "flang/Runtime/memory.h"
13
#include <algorithm>
14
#include <cerrno>
15
#include <cstring>
16
#include <fcntl.h>
17
#include <stdlib.h>
18
#include <sys/stat.h>
19
#ifdef _WIN32
20
#include "flang/Common/windows-include.h"
21
#include <io.h>
22
#else
23
#include <unistd.h>
24
#endif
25

26
namespace Fortran::runtime::io {
27

28
void OpenFile::set_path(OwningPtr<char> &&path, std::size_t bytes) {
29
  path_ = std::move(path);
30
  pathLength_ = bytes;
31
}
32

33
static int openfile_mkstemp(IoErrorHandler &handler) {
34
#ifdef _WIN32
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) {
43
    return -1;
44
  }
45
  if (::GetTempFileNameA(tempDirName, "Fortran", uUnique, tempFileName) == 0) {
46
    return -1;
47
  }
48
  int fd{::_open(tempFileName, _O_CREAT | _O_BINARY | _O_TEMPORARY | _O_RDWR,
49
      _S_IREAD | _S_IWRITE)};
50
#else
51
  char path[]{"/tmp/Fortran-Scratch-XXXXXX"};
52
  int fd{::mkstemp(path)};
53
#endif
54
  if (fd < 0) {
55
    handler.SignalErrno();
56
  }
57
#ifndef _WIN32
58
  ::unlink(path);
59
#endif
60
  return fd;
61
}
62

63
void OpenFile::Open(OpenStatus status, Fortran::common::optional<Action> action,
64
    Position position, IoErrorHandler &handler) {
65
  if (fd_ >= 0 &&
66
      (status == OpenStatus::Old || status == OpenStatus::Unknown)) {
67
    return;
68
  }
69
  CloseFd(handler);
70
  if (status == OpenStatus::Scratch) {
71
    if (path_.get()) {
72
      handler.SignalError("FILE= must not appear with STATUS='SCRATCH'");
73
      path_.reset();
74
    }
75
    if (!action) {
76
      action = Action::ReadWrite;
77
    }
78
    fd_ = openfile_mkstemp(handler);
79
  } else {
80
    if (!path_.get()) {
81
      handler.SignalError("FILE= is required");
82
      return;
83
    }
84
    int flags{0};
85
#ifdef _WIN32
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.
89
    flags |= O_BINARY;
90
#endif
91
    if (status != OpenStatus::Old) {
92
      flags |= O_CREAT;
93
    }
94
    if (status == OpenStatus::New) {
95
      flags |= O_EXCL;
96
    } else if (status == OpenStatus::Replace) {
97
      flags |= O_TRUNC;
98
    }
99
    if (!action) {
100
      // Try to open read/write, back off to read-only or even write-only
101
      // on failure
102
      fd_ = ::open(path_.get(), flags | O_RDWR, 0600);
103
      if (fd_ >= 0) {
104
        action = Action::ReadWrite;
105
      } else {
106
        fd_ = ::open(path_.get(), flags | O_RDONLY, 0600);
107
        if (fd_ >= 0) {
108
          action = Action::Read;
109
        } else {
110
          action = Action::Write;
111
        }
112
      }
113
    }
114
    if (fd_ < 0) {
115
      switch (*action) {
116
      case Action::Read:
117
        flags |= O_RDONLY;
118
        break;
119
      case Action::Write:
120
        flags |= O_WRONLY;
121
        break;
122
      case Action::ReadWrite:
123
        flags |= O_RDWR;
124
        break;
125
      }
126
      fd_ = ::open(path_.get(), flags, 0600);
127
      if (fd_ < 0) {
128
        handler.SignalErrno();
129
      }
130
    }
131
  }
132
  RUNTIME_CHECK(handler, action.has_value());
133
  pending_.reset();
134
  if (fd_ >= 0 && position == Position::Append && !RawSeekToEnd()) {
135
    handler.SignalError(IostatOpenBadAppend);
136
  }
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) {
141
    knownSize_.reset();
142
#ifndef _WIN32
143
    struct stat buf;
144
    if (fd_ >= 0 && ::fstat(fd_, &buf) == 0) {
145
      mayPosition_ = S_ISREG(buf.st_mode);
146
      knownSize_ = buf.st_size;
147
    }
148
#else // TODO: _WIN32
149
    mayPosition_ = true;
150
#endif
151
  } else {
152
    knownSize_ = 0;
153
    mayPosition_ = true;
154
  }
155
  openPosition_ = position; // for INQUIRE(POSITION=)
156
}
157

158
void OpenFile::Predefine(int fd) {
159
  fd_ = fd;
160
  path_.reset();
161
  pathLength_ = 0;
162
  position_ = 0;
163
  knownSize_.reset();
164
  nextId_ = 0;
165
  pending_.reset();
166
  isTerminal_ = IsATerminal(fd_) == 1;
167
  mayRead_ = fd == 0;
168
  mayWrite_ = fd != 0;
169
  mayPosition_ = false;
170
#ifdef _WIN32
171
  isWindowsTextFile_ = true;
172
#endif
173
}
174

175
void OpenFile::Close(CloseStatus status, IoErrorHandler &handler) {
176
  pending_.reset();
177
  knownSize_.reset();
178
  switch (status) {
179
  case CloseStatus::Keep:
180
    break;
181
  case CloseStatus::Delete:
182
    if (path_.get()) {
183
      ::unlink(path_.get());
184
    }
185
    break;
186
  }
187
  path_.reset();
188
  CloseFd(handler);
189
}
190

191
std::size_t OpenFile::Read(FileOffset at, char *buffer, std::size_t minBytes,
192
    std::size_t maxBytes, IoErrorHandler &handler) {
193
  if (maxBytes == 0) {
194
    return 0;
195
  }
196
  CheckOpen(handler);
197
  if (!Seek(at, handler)) {
198
    return 0;
199
  }
200
  minBytes = std::min(minBytes, maxBytes);
201
  std::size_t got{0};
202
  while (got < minBytes) {
203
    auto chunk{::read(fd_, buffer + got, maxBytes - got)};
204
    if (chunk == 0) {
205
      break;
206
    } else if (chunk < 0) {
207
      auto err{errno};
208
      if (err != EAGAIN && err != EWOULDBLOCK && err != EINTR) {
209
        handler.SignalError(err);
210
        break;
211
      }
212
    } else {
213
      SetPosition(position_ + chunk);
214
      got += chunk;
215
    }
216
  }
217
  return got;
218
}
219

220
std::size_t OpenFile::Write(FileOffset at, const char *buffer,
221
    std::size_t bytes, IoErrorHandler &handler) {
222
  if (bytes == 0) {
223
    return 0;
224
  }
225
  CheckOpen(handler);
226
  if (!Seek(at, handler)) {
227
    return 0;
228
  }
229
  std::size_t put{0};
230
  while (put < bytes) {
231
    auto chunk{::write(fd_, buffer + put, bytes - put)};
232
    if (chunk >= 0) {
233
      SetPosition(position_ + chunk);
234
      put += chunk;
235
    } else {
236
      auto err{errno};
237
      if (err != EAGAIN && err != EWOULDBLOCK && err != EINTR) {
238
        handler.SignalError(err);
239
        break;
240
      }
241
    }
242
  }
243
  if (knownSize_ && position_ > *knownSize_) {
244
    knownSize_ = position_;
245
  }
246
  return put;
247
}
248

249
inline static int openfile_ftruncate(int fd, OpenFile::FileOffset at) {
250
#ifdef _WIN32
251
  return ::_chsize(fd, at);
252
#else
253
  return ::ftruncate(fd, at);
254
#endif
255
}
256

257
void OpenFile::Truncate(FileOffset at, IoErrorHandler &handler) {
258
  CheckOpen(handler);
259
  if (!knownSize_ || *knownSize_ != at) {
260
    if (openfile_ftruncate(fd_, at) != 0) {
261
      handler.SignalErrno();
262
    }
263
    knownSize_ = at;
264
  }
265
}
266

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) {
272
  CheckOpen(handler);
273
  int iostat{0};
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)};
277
#else
278
    auto chunk{Seek(at, handler) ? ::read(fd_, buffer + got, bytes - got) : -1};
279
#endif
280
    if (chunk == 0) {
281
      iostat = FORTRAN_RUNTIME_IOSTAT_END;
282
      break;
283
    }
284
    if (chunk < 0) {
285
      auto err{errno};
286
      if (err != EAGAIN && err != EWOULDBLOCK && err != EINTR) {
287
        iostat = err;
288
        break;
289
      }
290
    } else {
291
      at += chunk;
292
      got += chunk;
293
    }
294
  }
295
  return PendingResult(handler, iostat);
296
}
297

298
// TODO: True asynchronicity
299
int OpenFile::WriteAsynchronously(FileOffset at, const char *buffer,
300
    std::size_t bytes, IoErrorHandler &handler) {
301
  CheckOpen(handler);
302
  int iostat{0};
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)};
306
#else
307
    auto chunk{
308
        Seek(at, handler) ? ::write(fd_, buffer + put, bytes - put) : -1};
309
#endif
310
    if (chunk >= 0) {
311
      at += chunk;
312
      put += chunk;
313
    } else {
314
      auto err{errno};
315
      if (err != EAGAIN && err != EWOULDBLOCK && err != EINTR) {
316
        iostat = err;
317
        break;
318
      }
319
    }
320
  }
321
  return PendingResult(handler, iostat);
322
}
323

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()) {
328
    if (p->id == id) {
329
      ioStat = p->ioStat;
330
      if (prev) {
331
        prev->next.reset(p->next.release());
332
      } else {
333
        pending_.reset(p->next.release());
334
      }
335
      break;
336
    }
337
  }
338
  if (ioStat) {
339
    handler.SignalError(*ioStat);
340
  }
341
}
342

343
void OpenFile::WaitAll(IoErrorHandler &handler) {
344
  while (true) {
345
    int ioStat;
346
    if (pending_) {
347
      ioStat = pending_->ioStat;
348
      pending_.reset(pending_->next.release());
349
    } else {
350
      return;
351
    }
352
    handler.SignalError(ioStat);
353
  }
354
}
355

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;
364
    } else {
365
      return Position::AsIs; // processor-dependent & no common behavior
366
    }
367
  }
368
}
369

370
void OpenFile::CheckOpen(const Terminator &terminator) {
371
  RUNTIME_CHECK(terminator, fd_ >= 0);
372
}
373

374
bool OpenFile::Seek(FileOffset at, IoErrorHandler &handler) {
375
  if (at == position_) {
376
    return true;
377
  } else if (RawSeek(at)) {
378
    SetPosition(at);
379
    return true;
380
  } else {
381
    handler.SignalError(IostatCannotReposition);
382
    return false;
383
  }
384
}
385

386
bool OpenFile::RawSeek(FileOffset at) {
387
#ifdef _LARGEFILE64_SOURCE
388
  return ::lseek64(fd_, at, SEEK_SET) == at;
389
#else
390
  return ::lseek(fd_, at, SEEK_SET) == at;
391
#endif
392
}
393

394
bool OpenFile::RawSeekToEnd() {
395
#ifdef _LARGEFILE64_SOURCE
396
  std::int64_t at{::lseek64(fd_, 0, SEEK_END)};
397
#else
398
  std::int64_t at{::lseek(fd_, 0, SEEK_END)};
399
#endif
400
  if (at >= 0) {
401
    knownSize_ = at;
402
    return true;
403
  } else {
404
    return false;
405
  }
406
}
407

408
int OpenFile::PendingResult(const Terminator &terminator, int iostat) {
409
  int id{nextId_++};
410
  pending_ = New<Pending>{terminator}(id, iostat, std::move(pending_));
411
  return id;
412
}
413

414
void OpenFile::CloseFd(IoErrorHandler &handler) {
415
  if (fd_ >= 0) {
416
    if (fd_ <= 2) {
417
      // don't actually close a standard file descriptor, we might need it
418
    } else {
419
      if (::close(fd_) != 0) {
420
        handler.SignalErrno();
421
      }
422
    }
423
    fd_ = -1;
424
  }
425
}
426

427
#if !defined(RT_DEVICE_COMPILATION)
428
bool IsATerminal(int fd) { return ::isatty(fd); }
429

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.
436
#define F_OK 00
437
#define W_OK 02
438
#define R_OK 04
439
#endif
440

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;
446
}
447

448
std::int64_t SizeInBytes(const char *path) {
449
#ifndef _WIN32
450
  struct stat buf;
451
  if (::stat(path, &buf) == 0) {
452
    return buf.st_size;
453
  }
454
#else // TODO: _WIN32
455
#endif
456
  // No Fortran compiler signals an error
457
  return -1;
458
}
459
#else // defined(RT_DEVICE_COMPILATION)
460
RT_API_ATTRS bool IsATerminal(int fd) {
461
  Terminator{__FILE__, __LINE__}.Crash("%s: unsupported", RT_PRETTY_FUNCTION);
462
}
463
RT_API_ATTRS bool IsExtant(const char *path) {
464
  Terminator{__FILE__, __LINE__}.Crash("%s: unsupported", RT_PRETTY_FUNCTION);
465
}
466
RT_API_ATTRS bool MayRead(const char *path) {
467
  Terminator{__FILE__, __LINE__}.Crash("%s: unsupported", RT_PRETTY_FUNCTION);
468
}
469
RT_API_ATTRS bool MayWrite(const char *path) {
470
  Terminator{__FILE__, __LINE__}.Crash("%s: unsupported", RT_PRETTY_FUNCTION);
471
}
472
RT_API_ATTRS bool MayReadAndWrite(const char *path) {
473
  Terminator{__FILE__, __LINE__}.Crash("%s: unsupported", RT_PRETTY_FUNCTION);
474
}
475
RT_API_ATTRS std::int64_t SizeInBytes(const char *path) {
476
  Terminator{__FILE__, __LINE__}.Crash("%s: unsupported", RT_PRETTY_FUNCTION);
477
}
478
#endif // defined(RT_DEVICE_COMPILATION)
479

480
} // namespace Fortran::runtime::io
481

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.