1 //===--- A platform independent file data structure -------------*- C++ -*-===// 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 #ifndef LLVM_LIBC_SRC___SUPPORT_FILE_FILE_H 10 #define LLVM_LIBC_SRC___SUPPORT_FILE_FILE_H 11 12 #include "hdr/stdio_macros.h" 13 #include "hdr/types/off_t.h" 14 #include "src/__support/CPP/new.h" 15 #include "src/__support/error_or.h" 16 #include "src/__support/macros/config.h" 17 #include "src/__support/macros/properties/architectures.h" 18 #include "src/__support/threads/mutex.h" 19 20 #include <stddef.h> 21 #include <stdint.h> 22 23 namespace LIBC_NAMESPACE_DECL { 24 25 struct FileIOResult { 26 size_t value; 27 int error; 28 FileIOResultFileIOResult29 constexpr FileIOResult(size_t val) : value(val), error(0) {} FileIOResultFileIOResult30 constexpr FileIOResult(size_t val, int error) : value(val), error(error) {} 31 has_errorFileIOResult32 constexpr bool has_error() { return error != 0; } 33 size_tFileIOResult34 constexpr operator size_t() { return value; } 35 }; 36 37 // This a generic base class to encapsulate a platform independent file data 38 // structure. Platform specific specializations should create a subclass as 39 // suitable for their platform. 40 class File { 41 public: 42 static constexpr size_t DEFAULT_BUFFER_SIZE = 1024; 43 44 using LockFunc = void(File *); 45 using UnlockFunc = void(File *); 46 47 using WriteFunc = FileIOResult(File *, const void *, size_t); 48 using ReadFunc = FileIOResult(File *, void *, size_t); 49 // The SeekFunc is expected to return the current offset of the external 50 // file position indicator. 51 using SeekFunc = ErrorOr<off_t>(File *, off_t, int); 52 using CloseFunc = int(File *); 53 54 using ModeFlags = uint32_t; 55 56 // The three different types of flags below are to be used with '|' operator. 57 // Their values correspond to mutually exclusive bits in a 32-bit unsigned 58 // integer value. A flag set can include both READ and WRITE if the file 59 // is opened in update mode (ie. if the file was opened with a '+' the mode 60 // string.) 61 enum class OpenMode : ModeFlags { 62 READ = 0x1, 63 WRITE = 0x2, 64 APPEND = 0x4, 65 PLUS = 0x8, 66 }; 67 68 // Denotes a file opened in binary mode (which is specified by including 69 // the 'b' character in teh mode string.) 70 enum class ContentType : ModeFlags { 71 BINARY = 0x10, 72 }; 73 74 // Denotes a file to be created for writing. 75 enum class CreateType : ModeFlags { 76 EXCLUSIVE = 0x100, 77 }; 78 79 private: 80 enum class FileOp : uint8_t { NONE, READ, WRITE, SEEK }; 81 82 // Platform specific functions which create new file objects should initialize 83 // these fields suitably via the constructor. Typically, they should be simple 84 // syscall wrappers for the corresponding functionality. 85 WriteFunc *platform_write; 86 ReadFunc *platform_read; 87 SeekFunc *platform_seek; 88 CloseFunc *platform_close; 89 90 Mutex mutex; 91 92 // For files which are readable, we should be able to support one ungetc 93 // operation even if |buf| is nullptr. So, in the constructor of File, we 94 // set |buf| to point to this buffer character. 95 uint8_t ungetc_buf; 96 97 uint8_t *buf; // Pointer to the stream buffer for buffered streams 98 size_t bufsize; // Size of the buffer pointed to by |buf|. 99 100 // Buffering mode to used to buffer. 101 int bufmode; 102 103 // If own_buf is true, the |buf| is owned by the stream and will be 104 // free-ed when close method is called on the stream. 105 bool own_buf; 106 107 // The mode in which the file was opened. 108 ModeFlags mode; 109 110 // Current read or write pointer. 111 size_t pos; 112 113 // Represents the previous operation that was performed. 114 FileOp prev_op; 115 116 // When the buffer is used as a read buffer, read_limit is the upper limit 117 // of the index to which the buffer can be read until. 118 size_t read_limit; 119 120 bool eof; 121 bool err; 122 123 // This is a convenience RAII class to lock and unlock file objects. 124 class FileLock { 125 File *file; 126 127 public: FileLock(File * f)128 explicit FileLock(File *f) : file(f) { file->lock(); } 129 ~FileLock()130 ~FileLock() { file->unlock(); } 131 132 FileLock(const FileLock &) = delete; 133 FileLock(FileLock &&) = delete; 134 }; 135 136 protected: write_allowed()137 constexpr bool write_allowed() const { 138 return mode & (static_cast<ModeFlags>(OpenMode::WRITE) | 139 static_cast<ModeFlags>(OpenMode::APPEND) | 140 static_cast<ModeFlags>(OpenMode::PLUS)); 141 } 142 read_allowed()143 constexpr bool read_allowed() const { 144 return mode & (static_cast<ModeFlags>(OpenMode::READ) | 145 static_cast<ModeFlags>(OpenMode::PLUS)); 146 } 147 148 public: 149 // We want this constructor to be constexpr so that global file objects 150 // like stdout do not require invocation of the constructor which can 151 // potentially lead to static initialization order fiasco. Consequently, 152 // we will assume that the |buffer| and |buffer_size| argument are 153 // meaningful - that is, |buffer| is nullptr if and only if |buffer_size| 154 // is zero. This way, we will not have to employ the semantics of 155 // the set_buffer method and allocate a buffer. File(WriteFunc * wf,ReadFunc * rf,SeekFunc * sf,CloseFunc * cf,uint8_t * buffer,size_t buffer_size,int buffer_mode,bool owned,ModeFlags modeflags)156 constexpr File(WriteFunc *wf, ReadFunc *rf, SeekFunc *sf, CloseFunc *cf, 157 uint8_t *buffer, size_t buffer_size, int buffer_mode, 158 bool owned, ModeFlags modeflags) 159 : platform_write(wf), platform_read(rf), platform_seek(sf), 160 platform_close(cf), mutex(/*timed=*/false, /*recursive=*/false, 161 /*robust=*/false, /*pshared=*/false), 162 ungetc_buf(0), buf(buffer), bufsize(buffer_size), bufmode(buffer_mode), 163 own_buf(owned), mode(modeflags), pos(0), prev_op(FileOp::NONE), 164 read_limit(0), eof(false), err(false) { 165 adjust_buf(); 166 } 167 168 // Buffered write of |len| bytes from |data| without the file lock. 169 FileIOResult write_unlocked(const void *data, size_t len); 170 171 // Buffered write of |len| bytes from |data| under the file lock. write(const void * data,size_t len)172 FileIOResult write(const void *data, size_t len) { 173 FileLock l(this); 174 return write_unlocked(data, len); 175 } 176 177 // Buffered read of |len| bytes into |data| without the file lock. 178 FileIOResult read_unlocked(void *data, size_t len); 179 180 // Buffered read of |len| bytes into |data| under the file lock. read(void * data,size_t len)181 FileIOResult read(void *data, size_t len) { 182 FileLock l(this); 183 return read_unlocked(data, len); 184 } 185 186 ErrorOr<int> seek(off_t offset, int whence); 187 188 ErrorOr<off_t> tell(); 189 190 // If buffer has data written to it, flush it out. Does nothing if the 191 // buffer is currently being used as a read buffer. flush()192 int flush() { 193 FileLock lock(this); 194 return flush_unlocked(); 195 } 196 197 int flush_unlocked(); 198 199 // Returns EOF on error and keeps the file unchanged. 200 int ungetc_unlocked(int c); 201 ungetc(int c)202 int ungetc(int c) { 203 FileLock lock(this); 204 return ungetc_unlocked(c); 205 } 206 207 // Does the following: 208 // 1. If in write mode, Write out any data present in the buffer. 209 // 2. Call platform_close. 210 // platform_close is expected to cleanup the complete file object. close()211 int close() { 212 { 213 FileLock lock(this); 214 if (prev_op == FileOp::WRITE && pos > 0) { 215 auto buf_result = platform_write(this, buf, pos); 216 if (buf_result.has_error() || buf_result.value < pos) { 217 err = true; 218 return buf_result.error; 219 } 220 } 221 } 222 223 // If we own the buffer, delete it before calling the platform close 224 // implementation. The platform close should not need to access the buffer 225 // and we need to clean it up before the entire structure is removed. 226 if (own_buf) 227 delete buf; 228 229 // Platform close is expected to cleanup the file data structure which 230 // includes the file mutex. Hence, we call platform_close after releasing 231 // the file lock. Another thread doing file operations while a thread is 232 // closing the file is undefined behavior as per POSIX. 233 return platform_close(this); 234 } 235 236 // Sets the internal buffer to |buffer| with buffering mode |mode|. 237 // |size| is the size of |buffer|. If |size| is non-zero, but |buffer| 238 // is nullptr, then a buffer owned by this file will be allocated. 239 // Else, |buffer| will not be owned by this file. 240 // 241 // Will return zero on success, or an error value on failure. Will fail 242 // if: 243 // 1. |buffer| is not a nullptr but |size| is zero. 244 // 2. |buffer_mode| is not one of _IOLBF, IOFBF or _IONBF. 245 // 3. If an allocation was required but the allocation failed. 246 // For cases 1 and 2, the error returned in EINVAL. For case 3, error returned 247 // is ENOMEM. 248 int set_buffer(void *buffer, size_t size, int buffer_mode); 249 lock()250 void lock() { mutex.lock(); } unlock()251 void unlock() { mutex.unlock(); } 252 error_unlocked()253 bool error_unlocked() const { return err; } 254 error()255 bool error() { 256 FileLock l(this); 257 return error_unlocked(); 258 } 259 clearerr_unlocked()260 void clearerr_unlocked() { err = false; } 261 clearerr()262 void clearerr() { 263 FileLock l(this); 264 clearerr_unlocked(); 265 } 266 iseof_unlocked()267 bool iseof_unlocked() { return eof; } 268 iseof()269 bool iseof() { 270 FileLock l(this); 271 return iseof_unlocked(); 272 } 273 274 // Returns an bit map of flags corresponding to enumerations of 275 // OpenMode, ContentType and CreateType. 276 static ModeFlags mode_flags(const char *mode); 277 278 private: 279 FileIOResult write_unlocked_lbf(const uint8_t *data, size_t len); 280 FileIOResult write_unlocked_fbf(const uint8_t *data, size_t len); 281 FileIOResult write_unlocked_nbf(const uint8_t *data, size_t len); 282 adjust_buf()283 constexpr void adjust_buf() { 284 if (read_allowed() && (buf == nullptr || bufsize == 0)) { 285 // We should allow atleast one ungetc operation. 286 // This might give an impression that a buffer will be used even when 287 // the user does not want a buffer. But, that will not be the case. 288 // For reading, the buffering does not come into play. For writing, let 289 // us take up the three different kinds of buffering separately: 290 // 1. If user wants _IOFBF but gives a zero buffer, buffering still 291 // happens in the OS layer until the user flushes. So, from the user's 292 // point of view, this single byte buffer does not affect their 293 // experience. 294 // 2. If user wants _IOLBF but gives a zero buffer, the reasoning is 295 // very similar to the _IOFBF case. 296 // 3. If user wants _IONBF, then the buffer is ignored for writing. 297 // So, all of the above cases, having a single ungetc buffer does not 298 // affect the behavior experienced by the user. 299 buf = &ungetc_buf; 300 bufsize = 1; 301 own_buf = false; // We shouldn't call free on |buf| when closing the file. 302 } 303 } 304 }; 305 306 // The implementaiton of this function is provided by the platform_file 307 // library. 308 ErrorOr<File *> openfile(const char *path, const char *mode); 309 310 // The platform_file library should implement it if it relevant for that 311 // platform. 312 int get_fileno(File *f); 313 314 extern File *stdin; 315 extern File *stdout; 316 extern File *stderr; 317 318 } // namespace LIBC_NAMESPACE_DECL 319 320 #endif // LLVM_LIBC_SRC___SUPPORT_FILE_FILE_H 321