14ef02da0SSiva Chandra Reddy //===--- Implementation of a platform independent file data structure -----===// 24ef02da0SSiva Chandra Reddy // 34ef02da0SSiva Chandra Reddy // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 44ef02da0SSiva Chandra Reddy // See https://llvm.org/LICENSE.txt for license information. 54ef02da0SSiva Chandra Reddy // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 64ef02da0SSiva Chandra Reddy // 74ef02da0SSiva Chandra Reddy //===----------------------------------------------------------------------===// 84ef02da0SSiva Chandra Reddy 94ef02da0SSiva Chandra Reddy #include "file.h" 104ef02da0SSiva Chandra Reddy 119cfe3028SJob Henandez Lara #include "hdr/func/realloc.h" 125aed6d67SMichael Jones #include "hdr/stdio_macros.h" 135aed6d67SMichael Jones #include "hdr/types/off_t.h" 14d06308dfSSiva Chandra Reddy #include "src/__support/CPP/new.h" 154d231912SGuillaume Chatelet #include "src/__support/CPP/span.h" 165ff3ff33SPetr Hosek #include "src/__support/macros/config.h" 1704a9c625SMichael Jones #include "src/errno/libc_errno.h" // For error macros 184ef02da0SSiva Chandra Reddy 195ff3ff33SPetr Hosek namespace LIBC_NAMESPACE_DECL { 204ef02da0SSiva Chandra Reddy 219beb8d11SMichael Jones FileIOResult File::write_unlocked(const void *data, size_t len) { 224ef02da0SSiva Chandra Reddy if (!write_allowed()) { 234ef02da0SSiva Chandra Reddy err = true; 249beb8d11SMichael Jones return {0, EBADF}; 254ef02da0SSiva Chandra Reddy } 264ef02da0SSiva Chandra Reddy 274ef02da0SSiva Chandra Reddy prev_op = FileOp::WRITE; 284ef02da0SSiva Chandra Reddy 29d04494ccSJoseph Huber if (bufmode == _IONBF) { // unbuffered. 304d231912SGuillaume Chatelet size_t ret_val = 314d231912SGuillaume Chatelet write_unlocked_nbf(static_cast<const uint8_t *>(data), len); 326ce490e5SMichael Jones flush_unlocked(); 336ce490e5SMichael Jones return ret_val; 34e6c401b5SJoseph Huber } else if (bufmode == _IOFBF) { // fully buffered 35e6c401b5SJoseph Huber return write_unlocked_fbf(static_cast<const uint8_t *>(data), len); 36e6c401b5SJoseph Huber } else /*if (bufmode == _IOLBF) */ { // line buffered 37e6c401b5SJoseph Huber return write_unlocked_lbf(static_cast<const uint8_t *>(data), len); 386ce490e5SMichael Jones } 396ce490e5SMichael Jones } 406ce490e5SMichael Jones 419beb8d11SMichael Jones FileIOResult File::write_unlocked_nbf(const uint8_t *data, size_t len) { 42d04494ccSJoseph Huber if (pos > 0) { // If the buffer is not empty 436ce490e5SMichael Jones // Flush the buffer 446ce490e5SMichael Jones const size_t write_size = pos; 45*c6b7bd42SJack Huang FileIOResult write_result = platform_write(this, buf, write_size); 466ce490e5SMichael Jones pos = 0; // Buffer is now empty so reset pos to the beginning. 476ce490e5SMichael Jones // If less bytes were written than expected, then an error occurred. 489beb8d11SMichael Jones if (write_result < write_size) { 496ce490e5SMichael Jones err = true; 509beb8d11SMichael Jones // No bytes from data were written, so return 0. 519beb8d11SMichael Jones return {0, write_result.error}; 526ce490e5SMichael Jones } 536ce490e5SMichael Jones } 546ce490e5SMichael Jones 55*c6b7bd42SJack Huang FileIOResult write_result = platform_write(this, data, len); 569beb8d11SMichael Jones if (write_result < len) 576ce490e5SMichael Jones err = true; 589beb8d11SMichael Jones return write_result; 596ce490e5SMichael Jones } 606ce490e5SMichael Jones 619beb8d11SMichael Jones FileIOResult File::write_unlocked_fbf(const uint8_t *data, size_t len) { 626ce490e5SMichael Jones const size_t init_pos = pos; 636ce490e5SMichael Jones const size_t bufspace = bufsize - pos; 646ce490e5SMichael Jones 656ce490e5SMichael Jones // If data is too large to be buffered at all, then just write it unbuffered. 666ce490e5SMichael Jones if (len > bufspace + bufsize) 676ce490e5SMichael Jones return write_unlocked_nbf(data, len); 686ce490e5SMichael Jones 696ce490e5SMichael Jones // we split |data| (conceptually) using the split point. Then we handle the 706ce490e5SMichael Jones // two pieces separately. 716ce490e5SMichael Jones const size_t split_point = len < bufspace ? len : bufspace; 726ce490e5SMichael Jones 736ce490e5SMichael Jones // The primary piece is the piece of |data| we want to write to the buffer 746ce490e5SMichael Jones // before flushing. It will always fit into the buffer, since the split point 756ce490e5SMichael Jones // is defined as being min(len, bufspace), and it will always exist if len is 766ce490e5SMichael Jones // non-zero. 774d231912SGuillaume Chatelet cpp::span<const uint8_t> primary(data, split_point); 786ce490e5SMichael Jones 796ce490e5SMichael Jones // The second piece is the remainder of |data|. It is written to the buffer if 806ce490e5SMichael Jones // it fits, or written directly to the output if it doesn't. If the primary 816ce490e5SMichael Jones // piece fits entirely in the buffer, the remainder may be nothing. 824d231912SGuillaume Chatelet cpp::span<const uint8_t> remainder( 836ce490e5SMichael Jones static_cast<const uint8_t *>(data) + split_point, len - split_point); 846ce490e5SMichael Jones 854d231912SGuillaume Chatelet cpp::span<uint8_t> bufref(static_cast<uint8_t *>(buf), bufsize); 864ef02da0SSiva Chandra Reddy 876ce490e5SMichael Jones // Copy the first piece into the buffer. 884ef02da0SSiva Chandra Reddy // TODO: Replace the for loop below with a call to internal memcpy. 896ce490e5SMichael Jones for (size_t i = 0; i < primary.size(); ++i) 906ce490e5SMichael Jones bufref[pos + i] = primary[i]; 916ce490e5SMichael Jones pos += primary.size(); 926ce490e5SMichael Jones 936ce490e5SMichael Jones // If there is no remainder, we can return early, since the first piece has 946ce490e5SMichael Jones // fit completely into the buffer. 956ce490e5SMichael Jones if (remainder.size() == 0) 964ef02da0SSiva Chandra Reddy return len; 974ef02da0SSiva Chandra Reddy 986ce490e5SMichael Jones // We need to flush the buffer now, since there is still data and the buffer 996ce490e5SMichael Jones // is full. 1006ce490e5SMichael Jones const size_t write_size = pos; 1019beb8d11SMichael Jones 102*c6b7bd42SJack Huang FileIOResult buf_result = platform_write(this, buf, write_size); 1039beb8d11SMichael Jones size_t bytes_written = buf_result.value; 1049beb8d11SMichael Jones 1054ef02da0SSiva Chandra Reddy pos = 0; // Buffer is now empty so reset pos to the beginning. 1066ce490e5SMichael Jones // If less bytes were written than expected, then an error occurred. Return 1076ce490e5SMichael Jones // the number of bytes that have been written from |data|. 1089beb8d11SMichael Jones if (buf_result.has_error() || bytes_written < write_size) { 1094ef02da0SSiva Chandra Reddy err = true; 1109beb8d11SMichael Jones return {bytes_written <= init_pos ? 0 : bytes_written - init_pos, 1119beb8d11SMichael Jones buf_result.error}; 1124ef02da0SSiva Chandra Reddy } 1134ef02da0SSiva Chandra Reddy 1146ce490e5SMichael Jones // The second piece is handled basically the same as the first, although we 1156ce490e5SMichael Jones // know that if the second piece has data in it then the buffer has been 1166ce490e5SMichael Jones // flushed, meaning that pos is always 0. 1176ce490e5SMichael Jones if (remainder.size() < bufsize) { 1184ef02da0SSiva Chandra Reddy // TODO: Replace the for loop below with a call to internal memcpy. 1196ce490e5SMichael Jones for (size_t i = 0; i < remainder.size(); ++i) 1206ce490e5SMichael Jones bufref[i] = remainder[i]; 1216ce490e5SMichael Jones pos = remainder.size(); 1226ce490e5SMichael Jones } else { 1239beb8d11SMichael Jones 124*c6b7bd42SJack Huang FileIOResult result = 125*c6b7bd42SJack Huang platform_write(this, remainder.data(), remainder.size()); 1269beb8d11SMichael Jones size_t bytes_written = buf_result.value; 1276ce490e5SMichael Jones 1286ce490e5SMichael Jones // If less bytes were written than expected, then an error occurred. Return 1296ce490e5SMichael Jones // the number of bytes that have been written from |data|. 1309beb8d11SMichael Jones if (result.has_error() || bytes_written < remainder.size()) { 1316ce490e5SMichael Jones err = true; 1329beb8d11SMichael Jones return {primary.size() + bytes_written, result.error}; 1336ce490e5SMichael Jones } 1346ce490e5SMichael Jones } 1356ce490e5SMichael Jones 1364ef02da0SSiva Chandra Reddy return len; 1374ef02da0SSiva Chandra Reddy } 1384ef02da0SSiva Chandra Reddy 1399beb8d11SMichael Jones FileIOResult File::write_unlocked_lbf(const uint8_t *data, size_t len) { 1404d231912SGuillaume Chatelet constexpr uint8_t NEWLINE_CHAR = '\n'; 1416ce490e5SMichael Jones size_t last_newline = len; 142a9e0dbefSMichael Jones for (size_t i = len; i >= 1; --i) { 1434d231912SGuillaume Chatelet if (data[i - 1] == NEWLINE_CHAR) { 1445bcda1d3SMichael Jones last_newline = i - 1; 1456ce490e5SMichael Jones break; 1464ef02da0SSiva Chandra Reddy } 1476ce490e5SMichael Jones } 1486ce490e5SMichael Jones 1496ce490e5SMichael Jones // If there is no newline, treat this as fully buffered. 1506ce490e5SMichael Jones if (last_newline == len) { 1516ce490e5SMichael Jones return write_unlocked_fbf(data, len); 1526ce490e5SMichael Jones } 1536ce490e5SMichael Jones 1546ce490e5SMichael Jones // we split |data| (conceptually) using the split point. Then we handle the 1556ce490e5SMichael Jones // two pieces separately. 1566ce490e5SMichael Jones const size_t split_point = last_newline + 1; 1576ce490e5SMichael Jones 1586ce490e5SMichael Jones // The primary piece is everything in |data| up to the newline. It's written 1596ce490e5SMichael Jones // unbuffered to the output. 1604d231912SGuillaume Chatelet cpp::span<const uint8_t> primary(data, split_point); 1616ce490e5SMichael Jones 1626ce490e5SMichael Jones // The second piece is the remainder of |data|. It is written fully buffered, 1636ce490e5SMichael Jones // meaning it may stay in the buffer if it fits. 1644d231912SGuillaume Chatelet cpp::span<const uint8_t> remainder( 1656ce490e5SMichael Jones static_cast<const uint8_t *>(data) + split_point, len - split_point); 1666ce490e5SMichael Jones 1676ce490e5SMichael Jones size_t written = 0; 1686ce490e5SMichael Jones 1696ce490e5SMichael Jones written = write_unlocked_nbf(primary.data(), primary.size()); 1706ce490e5SMichael Jones if (written < primary.size()) { 1716ce490e5SMichael Jones err = true; 1726ce490e5SMichael Jones return written; 1736ce490e5SMichael Jones } 1746ce490e5SMichael Jones 1756ce490e5SMichael Jones flush_unlocked(); 1766ce490e5SMichael Jones 1776ce490e5SMichael Jones written += write_unlocked_fbf(remainder.data(), remainder.size()); 1780f72a0d2SSiva Chandra Reddy if (written < len) { 1796ce490e5SMichael Jones err = true; 1806ce490e5SMichael Jones return written; 1816ce490e5SMichael Jones } 1826ce490e5SMichael Jones 1834ef02da0SSiva Chandra Reddy return len; 1844ef02da0SSiva Chandra Reddy } 1854ef02da0SSiva Chandra Reddy 1869beb8d11SMichael Jones FileIOResult File::read_unlocked(void *data, size_t len) { 1874ef02da0SSiva Chandra Reddy if (!read_allowed()) { 1884ef02da0SSiva Chandra Reddy err = true; 1899beb8d11SMichael Jones return {0, EBADF}; 1904ef02da0SSiva Chandra Reddy } 1914ef02da0SSiva Chandra Reddy 1924ef02da0SSiva Chandra Reddy prev_op = FileOp::READ; 1934ef02da0SSiva Chandra Reddy 194*c6b7bd42SJack Huang if (bufmode == _IONBF) { // unbuffered. 195*c6b7bd42SJack Huang return read_unlocked_nbf(static_cast<uint8_t *>(data), len); 196*c6b7bd42SJack Huang } else if (bufmode == _IOFBF) { // fully buffered 197*c6b7bd42SJack Huang return read_unlocked_fbf(static_cast<uint8_t *>(data), len); 198*c6b7bd42SJack Huang } else /*if (bufmode == _IOLBF) */ { // line buffered 199*c6b7bd42SJack Huang // There is no line buffered mode for read. Use fully buffered instead. 200*c6b7bd42SJack Huang return read_unlocked_fbf(static_cast<uint8_t *>(data), len); 201*c6b7bd42SJack Huang } 202*c6b7bd42SJack Huang } 203*c6b7bd42SJack Huang 204*c6b7bd42SJack Huang size_t File::copy_data_from_buf(uint8_t *data, size_t len) { 2054d231912SGuillaume Chatelet cpp::span<uint8_t> bufref(static_cast<uint8_t *>(buf), bufsize); 2064d231912SGuillaume Chatelet cpp::span<uint8_t> dataref(static_cast<uint8_t *>(data), len); 2074ef02da0SSiva Chandra Reddy 2084ef02da0SSiva Chandra Reddy // Because read_limit is always greater than equal to pos, 2094ef02da0SSiva Chandra Reddy // available_data is never a wrapped around value. 2104ef02da0SSiva Chandra Reddy size_t available_data = read_limit - pos; 2114ef02da0SSiva Chandra Reddy if (len <= available_data) { 2124ef02da0SSiva Chandra Reddy // TODO: Replace the for loop below with a call to internal memcpy. 2134ef02da0SSiva Chandra Reddy for (size_t i = 0; i < len; ++i) 2144ef02da0SSiva Chandra Reddy dataref[i] = bufref[i + pos]; 2154ef02da0SSiva Chandra Reddy pos += len; 2164ef02da0SSiva Chandra Reddy return len; 2174ef02da0SSiva Chandra Reddy } 2184ef02da0SSiva Chandra Reddy 2194ef02da0SSiva Chandra Reddy // Copy all of the available data. 2204ef02da0SSiva Chandra Reddy // TODO: Replace the for loop with a call to internal memcpy. 2214ef02da0SSiva Chandra Reddy for (size_t i = 0; i < available_data; ++i) 2224ef02da0SSiva Chandra Reddy dataref[i] = bufref[i + pos]; 2234ef02da0SSiva Chandra Reddy read_limit = pos = 0; // Reset the pointers. 224*c6b7bd42SJack Huang 225*c6b7bd42SJack Huang return available_data; 226*c6b7bd42SJack Huang } 227*c6b7bd42SJack Huang 228*c6b7bd42SJack Huang FileIOResult File::read_unlocked_fbf(uint8_t *data, size_t len) { 229*c6b7bd42SJack Huang // Read data from the buffer first. 230*c6b7bd42SJack Huang size_t available_data = copy_data_from_buf(data, len); 231*c6b7bd42SJack Huang if (available_data == len) 232*c6b7bd42SJack Huang return available_data; 233*c6b7bd42SJack Huang 2341ceafe5eSSiva Chandra Reddy // Update the dataref to reflect that fact that we have already 2351ceafe5eSSiva Chandra Reddy // copied |available_data| into |data|. 2364ef02da0SSiva Chandra Reddy size_t to_fetch = len - available_data; 237*c6b7bd42SJack Huang cpp::span<uint8_t> dataref(static_cast<uint8_t *>(data) + available_data, 238*c6b7bd42SJack Huang to_fetch); 239*c6b7bd42SJack Huang 2404ef02da0SSiva Chandra Reddy if (to_fetch > bufsize) { 241*c6b7bd42SJack Huang FileIOResult result = platform_read(this, dataref.data(), to_fetch); 2429beb8d11SMichael Jones size_t fetched_size = result.value; 2439beb8d11SMichael Jones if (result.has_error() || fetched_size < to_fetch) { 2449beb8d11SMichael Jones if (!result.has_error()) 2454ef02da0SSiva Chandra Reddy eof = true; 2464ef02da0SSiva Chandra Reddy else 2474ef02da0SSiva Chandra Reddy err = true; 248*c6b7bd42SJack Huang return {available_data + fetched_size, result.error}; 2494ef02da0SSiva Chandra Reddy } 2504ef02da0SSiva Chandra Reddy return len; 2514ef02da0SSiva Chandra Reddy } 2524ef02da0SSiva Chandra Reddy 2534ef02da0SSiva Chandra Reddy // Fetch and buffer another buffer worth of data. 254*c6b7bd42SJack Huang FileIOResult result = platform_read(this, buf, bufsize); 2559beb8d11SMichael Jones size_t fetched_size = result.value; 2564ef02da0SSiva Chandra Reddy read_limit += fetched_size; 2574ef02da0SSiva Chandra Reddy size_t transfer_size = fetched_size >= to_fetch ? to_fetch : fetched_size; 2584ef02da0SSiva Chandra Reddy for (size_t i = 0; i < transfer_size; ++i) 259*c6b7bd42SJack Huang dataref[i] = buf[i]; 2604ef02da0SSiva Chandra Reddy pos += transfer_size; 2619beb8d11SMichael Jones if (result.has_error() || fetched_size < to_fetch) { 2629beb8d11SMichael Jones if (!result.has_error()) 2634ef02da0SSiva Chandra Reddy eof = true; 2644ef02da0SSiva Chandra Reddy else 2654ef02da0SSiva Chandra Reddy err = true; 2664ef02da0SSiva Chandra Reddy } 2679beb8d11SMichael Jones return {transfer_size + available_data, result.error}; 2684ef02da0SSiva Chandra Reddy } 2694ef02da0SSiva Chandra Reddy 270*c6b7bd42SJack Huang FileIOResult File::read_unlocked_nbf(uint8_t *data, size_t len) { 271*c6b7bd42SJack Huang // Check whether there is a character in the ungetc buffer. 272*c6b7bd42SJack Huang size_t available_data = copy_data_from_buf(data, len); 273*c6b7bd42SJack Huang if (available_data == len) 274*c6b7bd42SJack Huang return available_data; 275*c6b7bd42SJack Huang 276*c6b7bd42SJack Huang // Directly copy the data into |data|. 277*c6b7bd42SJack Huang cpp::span<uint8_t> dataref(static_cast<uint8_t *>(data) + available_data, 278*c6b7bd42SJack Huang len - available_data); 279*c6b7bd42SJack Huang FileIOResult result = platform_read(this, dataref.data(), dataref.size()); 280*c6b7bd42SJack Huang 281*c6b7bd42SJack Huang if (result.has_error() || result < dataref.size()) { 282*c6b7bd42SJack Huang if (!result.has_error()) 283*c6b7bd42SJack Huang eof = true; 284*c6b7bd42SJack Huang else 285*c6b7bd42SJack Huang err = true; 286*c6b7bd42SJack Huang } 287*c6b7bd42SJack Huang return {result + available_data, result.error}; 288*c6b7bd42SJack Huang } 289*c6b7bd42SJack Huang 2901ceafe5eSSiva Chandra Reddy int File::ungetc_unlocked(int c) { 2911ceafe5eSSiva Chandra Reddy // There is no meaning to unget if: 2921ceafe5eSSiva Chandra Reddy // 1. You are trying to push back EOF. 2931ceafe5eSSiva Chandra Reddy // 2. Read operations are not allowed on this file. 2941ceafe5eSSiva Chandra Reddy // 3. The previous operation was a write operation. 2951ceafe5eSSiva Chandra Reddy if (c == EOF || !read_allowed() || (prev_op == FileOp::WRITE)) 2961ceafe5eSSiva Chandra Reddy return EOF; 2971ceafe5eSSiva Chandra Reddy 2981ceafe5eSSiva Chandra Reddy cpp::span<uint8_t> bufref(static_cast<uint8_t *>(buf), bufsize); 2991ceafe5eSSiva Chandra Reddy if (read_limit == 0) { 3001ceafe5eSSiva Chandra Reddy // If |read_limit| is zero, it can mean three things: 3011ceafe5eSSiva Chandra Reddy // a. This file was just created. 3021ceafe5eSSiva Chandra Reddy // b. The previous operation was a seek operation. 3031ceafe5eSSiva Chandra Reddy // c. The previous operation was a read operation which emptied 3041ceafe5eSSiva Chandra Reddy // the buffer. 3051ceafe5eSSiva Chandra Reddy // For all the above cases, we simply write |c| at the beginning 3061ceafe5eSSiva Chandra Reddy // of the buffer and bump |read_limit|. Note that |pos| will also 3071ceafe5eSSiva Chandra Reddy // be zero in this case, so we don't need to adjust it. 3081ceafe5eSSiva Chandra Reddy bufref[0] = static_cast<unsigned char>(c); 3091ceafe5eSSiva Chandra Reddy ++read_limit; 3101ceafe5eSSiva Chandra Reddy } else { 3111ceafe5eSSiva Chandra Reddy // If |read_limit| is non-zero, it means that there is data in the buffer 3121ceafe5eSSiva Chandra Reddy // from a previous read operation. Which would also mean that |pos| is not 3131ceafe5eSSiva Chandra Reddy // zero. So, we decrement |pos| and write |c| in to the buffer at the new 3141ceafe5eSSiva Chandra Reddy // |pos|. If too many ungetc operations are performed without reads, it 3151ceafe5eSSiva Chandra Reddy // can lead to (pos == 0 but read_limit != 0). We will just error out in 3161ceafe5eSSiva Chandra Reddy // such a case. 3171ceafe5eSSiva Chandra Reddy if (pos == 0) 3181ceafe5eSSiva Chandra Reddy return EOF; 3191ceafe5eSSiva Chandra Reddy --pos; 3201ceafe5eSSiva Chandra Reddy bufref[pos] = static_cast<unsigned char>(c); 3211ceafe5eSSiva Chandra Reddy } 3221ceafe5eSSiva Chandra Reddy 3231ceafe5eSSiva Chandra Reddy eof = false; // There is atleast one character that can be read now. 3241ceafe5eSSiva Chandra Reddy err = false; // This operation was a success. 3251ceafe5eSSiva Chandra Reddy return c; 3261ceafe5eSSiva Chandra Reddy } 3271ceafe5eSSiva Chandra Reddy 3282308c7f5SMikhail R. Gadelha ErrorOr<int> File::seek(off_t offset, int whence) { 3294ef02da0SSiva Chandra Reddy FileLock lock(this); 3304ef02da0SSiva Chandra Reddy if (prev_op == FileOp::WRITE && pos > 0) { 3319beb8d11SMichael Jones 332*c6b7bd42SJack Huang FileIOResult buf_result = platform_write(this, buf, pos); 3339beb8d11SMichael Jones if (buf_result.has_error() || buf_result.value < pos) { 3344ef02da0SSiva Chandra Reddy err = true; 3359beb8d11SMichael Jones return Error(buf_result.error); 3364ef02da0SSiva Chandra Reddy } 337a0f6d12cSSiva Chandra Reddy } else if (prev_op == FileOp::READ && whence == SEEK_CUR) { 338a0f6d12cSSiva Chandra Reddy // More data could have been read out from the platform file than was 339a0f6d12cSSiva Chandra Reddy // required. So, we have to adjust the offset we pass to platform seek 340a0f6d12cSSiva Chandra Reddy // function. Note that read_limit >= pos is always true. 341a0f6d12cSSiva Chandra Reddy offset -= (read_limit - pos); 3424ef02da0SSiva Chandra Reddy } 3434ef02da0SSiva Chandra Reddy pos = read_limit = 0; 3444ef02da0SSiva Chandra Reddy prev_op = FileOp::SEEK; 3454ef02da0SSiva Chandra Reddy // Reset the eof flag as a seek might move the file positon to some place 3464ef02da0SSiva Chandra Reddy // readable. 3474ef02da0SSiva Chandra Reddy eof = false; 3489beb8d11SMichael Jones auto result = platform_seek(this, offset, whence); 3499beb8d11SMichael Jones if (!result.has_value()) 3509beb8d11SMichael Jones return Error(result.error()); 3519beb8d11SMichael Jones return 0; 35243e52ad5SSiva Chandra Reddy } 35343e52ad5SSiva Chandra Reddy 3547776fba4SMikhail R. Gadelha ErrorOr<off_t> File::tell() { 35543e52ad5SSiva Chandra Reddy FileLock lock(this); 3569beb8d11SMichael Jones auto seek_target = eof ? SEEK_END : SEEK_CUR; 3579beb8d11SMichael Jones auto result = platform_seek(this, 0, seek_target); 3589beb8d11SMichael Jones if (!result.has_value() || result.value() < 0) 3599beb8d11SMichael Jones return Error(result.error()); 3607776fba4SMikhail R. Gadelha off_t platform_offset = result.value(); 36143e52ad5SSiva Chandra Reddy if (prev_op == FileOp::READ) 36243e52ad5SSiva Chandra Reddy return platform_offset - (read_limit - pos); 3637776fba4SMikhail R. Gadelha if (prev_op == FileOp::WRITE) 36443e52ad5SSiva Chandra Reddy return platform_offset + pos; 36543e52ad5SSiva Chandra Reddy return platform_offset; 3664ef02da0SSiva Chandra Reddy } 3674ef02da0SSiva Chandra Reddy 3686ce490e5SMichael Jones int File::flush_unlocked() { 3694ef02da0SSiva Chandra Reddy if (prev_op == FileOp::WRITE && pos > 0) { 370*c6b7bd42SJack Huang FileIOResult buf_result = platform_write(this, buf, pos); 3719beb8d11SMichael Jones if (buf_result.has_error() || buf_result.value < pos) { 3724ef02da0SSiva Chandra Reddy err = true; 3739beb8d11SMichael Jones return buf_result.error; 3744ef02da0SSiva Chandra Reddy } 3754ef02da0SSiva Chandra Reddy pos = 0; 3764ef02da0SSiva Chandra Reddy } 37722f9dca1SSiva Chandra Reddy // TODO: Add POSIX behavior for input streams. 3784ef02da0SSiva Chandra Reddy return 0; 3794ef02da0SSiva Chandra Reddy } 3804ef02da0SSiva Chandra Reddy 3814eea8849SSiva Chandra Reddy int File::set_buffer(void *buffer, size_t size, int buffer_mode) { 3824eea8849SSiva Chandra Reddy // We do not need to lock the file as this method should be called before 3834eea8849SSiva Chandra Reddy // other operations are performed on the file. 3844eea8849SSiva Chandra Reddy if (buffer != nullptr && size == 0) 3854eea8849SSiva Chandra Reddy return EINVAL; 3864eea8849SSiva Chandra Reddy 3874eea8849SSiva Chandra Reddy switch (buffer_mode) { 3884eea8849SSiva Chandra Reddy case _IOFBF: 3894eea8849SSiva Chandra Reddy case _IOLBF: 3904eea8849SSiva Chandra Reddy case _IONBF: 3914eea8849SSiva Chandra Reddy break; 3924eea8849SSiva Chandra Reddy default: 3934eea8849SSiva Chandra Reddy return EINVAL; 3944eea8849SSiva Chandra Reddy } 3954eea8849SSiva Chandra Reddy 3964eea8849SSiva Chandra Reddy if (buffer == nullptr && size != 0 && buffer_mode != _IONBF) { 3974eea8849SSiva Chandra Reddy // We exclude the case of buffer_mode == _IONBF in this branch 3984eea8849SSiva Chandra Reddy // because we don't need to allocate buffer in such a case. 3994eea8849SSiva Chandra Reddy if (own_buf) { 400d06308dfSSiva Chandra Reddy // This is one of the places where use a C allocation functon 401d06308dfSSiva Chandra Reddy // as C++ does not have an equivalent of realloc. 402d06308dfSSiva Chandra Reddy buf = reinterpret_cast<uint8_t *>(realloc(buf, size)); 403d06308dfSSiva Chandra Reddy if (buf == nullptr) 404d06308dfSSiva Chandra Reddy return ENOMEM; 4054eea8849SSiva Chandra Reddy } else { 406d06308dfSSiva Chandra Reddy AllocChecker ac; 407d06308dfSSiva Chandra Reddy buf = new (ac) uint8_t[size]; 408d06308dfSSiva Chandra Reddy if (!ac) 409d06308dfSSiva Chandra Reddy return ENOMEM; 4104eea8849SSiva Chandra Reddy own_buf = true; 4114eea8849SSiva Chandra Reddy } 4124eea8849SSiva Chandra Reddy bufsize = size; 4134eea8849SSiva Chandra Reddy // TODO: Handle allocation failures. 4144eea8849SSiva Chandra Reddy } else { 4154ef02da0SSiva Chandra Reddy if (own_buf) 416d06308dfSSiva Chandra Reddy delete buf; 4174eea8849SSiva Chandra Reddy if (buffer_mode != _IONBF) { 4184d231912SGuillaume Chatelet buf = static_cast<uint8_t *>(buffer); 4194ef02da0SSiva Chandra Reddy bufsize = size; 4204eea8849SSiva Chandra Reddy } else { 4214eea8849SSiva Chandra Reddy // We don't need any buffer. 4224eea8849SSiva Chandra Reddy buf = nullptr; 4234eea8849SSiva Chandra Reddy bufsize = 0; 4244eea8849SSiva Chandra Reddy } 4254eea8849SSiva Chandra Reddy own_buf = false; 4264eea8849SSiva Chandra Reddy } 4274eea8849SSiva Chandra Reddy bufmode = buffer_mode; 4284eea8849SSiva Chandra Reddy adjust_buf(); 4294eea8849SSiva Chandra Reddy return 0; 4304ef02da0SSiva Chandra Reddy } 4314ef02da0SSiva Chandra Reddy 4324ef02da0SSiva Chandra Reddy File::ModeFlags File::mode_flags(const char *mode) { 4334ef02da0SSiva Chandra Reddy // First character in |mode| should be 'a', 'r' or 'w'. 4344ef02da0SSiva Chandra Reddy if (*mode != 'a' && *mode != 'r' && *mode != 'w') 4354ef02da0SSiva Chandra Reddy return 0; 4364ef02da0SSiva Chandra Reddy 4374ef02da0SSiva Chandra Reddy // There should be exaclty one main mode ('a', 'r' or 'w') character. 4384ef02da0SSiva Chandra Reddy // If there are more than one main mode characters listed, then 4394ef02da0SSiva Chandra Reddy // we will consider |mode| as incorrect and return 0; 4404ef02da0SSiva Chandra Reddy int main_mode_count = 0; 4414ef02da0SSiva Chandra Reddy 4424ef02da0SSiva Chandra Reddy ModeFlags flags = 0; 4434ef02da0SSiva Chandra Reddy for (; *mode != '\0'; ++mode) { 4444ef02da0SSiva Chandra Reddy switch (*mode) { 4454ef02da0SSiva Chandra Reddy case 'r': 4464ef02da0SSiva Chandra Reddy flags |= static_cast<ModeFlags>(OpenMode::READ); 4474ef02da0SSiva Chandra Reddy ++main_mode_count; 4484ef02da0SSiva Chandra Reddy break; 4494ef02da0SSiva Chandra Reddy case 'w': 4504ef02da0SSiva Chandra Reddy flags |= static_cast<ModeFlags>(OpenMode::WRITE); 4514ef02da0SSiva Chandra Reddy ++main_mode_count; 4524ef02da0SSiva Chandra Reddy break; 4534ef02da0SSiva Chandra Reddy case '+': 45429a631a2SSiva Chandra Reddy flags |= static_cast<ModeFlags>(OpenMode::PLUS); 4554ef02da0SSiva Chandra Reddy break; 4564ef02da0SSiva Chandra Reddy case 'b': 4574ef02da0SSiva Chandra Reddy flags |= static_cast<ModeFlags>(ContentType::BINARY); 4584ef02da0SSiva Chandra Reddy break; 4594ef02da0SSiva Chandra Reddy case 'a': 4604ef02da0SSiva Chandra Reddy flags |= static_cast<ModeFlags>(OpenMode::APPEND); 4614ef02da0SSiva Chandra Reddy ++main_mode_count; 4624ef02da0SSiva Chandra Reddy break; 4634ef02da0SSiva Chandra Reddy case 'x': 4644ef02da0SSiva Chandra Reddy flags |= static_cast<ModeFlags>(CreateType::EXCLUSIVE); 4654ef02da0SSiva Chandra Reddy break; 4664ef02da0SSiva Chandra Reddy default: 4674ef02da0SSiva Chandra Reddy return 0; 4684ef02da0SSiva Chandra Reddy } 4694ef02da0SSiva Chandra Reddy } 4704ef02da0SSiva Chandra Reddy 4714ef02da0SSiva Chandra Reddy if (main_mode_count != 1) 4724ef02da0SSiva Chandra Reddy return 0; 4734ef02da0SSiva Chandra Reddy 4744ef02da0SSiva Chandra Reddy return flags; 4754ef02da0SSiva Chandra Reddy } 4764ef02da0SSiva Chandra Reddy 4775ff3ff33SPetr Hosek } // namespace LIBC_NAMESPACE_DECL 478