• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===--- Implementation of a platform independent file data structure -----===//
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 
11 #include "src/__support/CPP/new.h"
12 #include "src/__support/CPP/span.h"
13 #include "src/errno/libc_errno.h" // For error macros
14 
15 #include <stdio.h>
16 #include <stdlib.h>
17 
18 namespace LIBC_NAMESPACE {
19 
write_unlocked(const void * data,size_t len)20 FileIOResult File::write_unlocked(const void *data, size_t len) {
21   if (!write_allowed()) {
22     err = true;
23     return {0, EBADF};
24   }
25 
26   prev_op = FileOp::WRITE;
27 
28   if (bufmode == _IONBF) { // unbuffered.
29     size_t ret_val =
30         write_unlocked_nbf(static_cast<const uint8_t *>(data), len);
31     flush_unlocked();
32     return ret_val;
33   } else if (bufmode == _IOFBF) { // fully buffered
34     return write_unlocked_fbf(static_cast<const uint8_t *>(data), len);
35   } else /*if (bufmode == _IOLBF) */ { // line buffered
36     return write_unlocked_lbf(static_cast<const uint8_t *>(data), len);
37   }
38 }
39 
write_unlocked_nbf(const uint8_t * data,size_t len)40 FileIOResult File::write_unlocked_nbf(const uint8_t *data, size_t len) {
41   if (pos > 0) { // If the buffer is not empty
42     // Flush the buffer
43     const size_t write_size = pos;
44     auto write_result = platform_write(this, buf, write_size);
45     pos = 0; // Buffer is now empty so reset pos to the beginning.
46     // If less bytes were written than expected, then an error occurred.
47     if (write_result < write_size) {
48       err = true;
49       // No bytes from data were written, so return 0.
50       return {0, write_result.error};
51     }
52   }
53 
54   auto write_result = platform_write(this, data, len);
55   if (write_result < len)
56     err = true;
57   return write_result;
58 }
59 
write_unlocked_fbf(const uint8_t * data,size_t len)60 FileIOResult File::write_unlocked_fbf(const uint8_t *data, size_t len) {
61   const size_t init_pos = pos;
62   const size_t bufspace = bufsize - pos;
63 
64   // If data is too large to be buffered at all, then just write it unbuffered.
65   if (len > bufspace + bufsize)
66     return write_unlocked_nbf(data, len);
67 
68   // we split |data| (conceptually) using the split point. Then we handle the
69   // two pieces separately.
70   const size_t split_point = len < bufspace ? len : bufspace;
71 
72   // The primary piece is the piece of |data| we want to write to the buffer
73   // before flushing. It will always fit into the buffer, since the split point
74   // is defined as being min(len, bufspace), and it will always exist if len is
75   // non-zero.
76   cpp::span<const uint8_t> primary(data, split_point);
77 
78   // The second piece is the remainder of |data|. It is written to the buffer if
79   // it fits, or written directly to the output if it doesn't. If the primary
80   // piece fits entirely in the buffer, the remainder may be nothing.
81   cpp::span<const uint8_t> remainder(
82       static_cast<const uint8_t *>(data) + split_point, len - split_point);
83 
84   cpp::span<uint8_t> bufref(static_cast<uint8_t *>(buf), bufsize);
85 
86   // Copy the first piece into the buffer.
87   // TODO: Replace the for loop below with a call to internal memcpy.
88   for (size_t i = 0; i < primary.size(); ++i)
89     bufref[pos + i] = primary[i];
90   pos += primary.size();
91 
92   // If there is no remainder, we can return early, since the first piece has
93   // fit completely into the buffer.
94   if (remainder.size() == 0)
95     return len;
96 
97   // We need to flush the buffer now, since there is still data and the buffer
98   // is full.
99   const size_t write_size = pos;
100 
101   auto buf_result = platform_write(this, buf, write_size);
102   size_t bytes_written = buf_result.value;
103 
104   pos = 0; // Buffer is now empty so reset pos to the beginning.
105   // If less bytes were written than expected, then an error occurred. Return
106   // the number of bytes that have been written from |data|.
107   if (buf_result.has_error() || bytes_written < write_size) {
108     err = true;
109     return {bytes_written <= init_pos ? 0 : bytes_written - init_pos,
110             buf_result.error};
111   }
112 
113   // The second piece is handled basically the same as the first, although we
114   // know that if the second piece has data in it then the buffer has been
115   // flushed, meaning that pos is always 0.
116   if (remainder.size() < bufsize) {
117     // TODO: Replace the for loop below with a call to internal memcpy.
118     for (size_t i = 0; i < remainder.size(); ++i)
119       bufref[i] = remainder[i];
120     pos = remainder.size();
121   } else {
122 
123     auto result = platform_write(this, remainder.data(), remainder.size());
124     size_t bytes_written = buf_result.value;
125 
126     // If less bytes were written than expected, then an error occurred. Return
127     // the number of bytes that have been written from |data|.
128     if (result.has_error() || bytes_written < remainder.size()) {
129       err = true;
130       return {primary.size() + bytes_written, result.error};
131     }
132   }
133 
134   return len;
135 }
136 
write_unlocked_lbf(const uint8_t * data,size_t len)137 FileIOResult File::write_unlocked_lbf(const uint8_t *data, size_t len) {
138   constexpr uint8_t NEWLINE_CHAR = '\n';
139   size_t last_newline = len;
140   for (size_t i = len; i >= 1; --i) {
141     if (data[i - 1] == NEWLINE_CHAR) {
142       last_newline = i - 1;
143       break;
144     }
145   }
146 
147   // If there is no newline, treat this as fully buffered.
148   if (last_newline == len) {
149     return write_unlocked_fbf(data, len);
150   }
151 
152   // we split |data| (conceptually) using the split point. Then we handle the
153   // two pieces separately.
154   const size_t split_point = last_newline + 1;
155 
156   // The primary piece is everything in |data| up to the newline. It's written
157   // unbuffered to the output.
158   cpp::span<const uint8_t> primary(data, split_point);
159 
160   // The second piece is the remainder of |data|. It is written fully buffered,
161   // meaning it may stay in the buffer if it fits.
162   cpp::span<const uint8_t> remainder(
163       static_cast<const uint8_t *>(data) + split_point, len - split_point);
164 
165   size_t written = 0;
166 
167   written = write_unlocked_nbf(primary.data(), primary.size());
168   if (written < primary.size()) {
169     err = true;
170     return written;
171   }
172 
173   flush_unlocked();
174 
175   written += write_unlocked_fbf(remainder.data(), remainder.size());
176   if (written < len) {
177     err = true;
178     return written;
179   }
180 
181   return len;
182 }
183 
read_unlocked(void * data,size_t len)184 FileIOResult File::read_unlocked(void *data, size_t len) {
185   if (!read_allowed()) {
186     err = true;
187     return {0, EBADF};
188   }
189 
190   prev_op = FileOp::READ;
191 
192   cpp::span<uint8_t> bufref(static_cast<uint8_t *>(buf), bufsize);
193   cpp::span<uint8_t> dataref(static_cast<uint8_t *>(data), len);
194 
195   // Because read_limit is always greater than equal to pos,
196   // available_data is never a wrapped around value.
197   size_t available_data = read_limit - pos;
198   if (len <= available_data) {
199     // TODO: Replace the for loop below with a call to internal memcpy.
200     for (size_t i = 0; i < len; ++i)
201       dataref[i] = bufref[i + pos];
202     pos += len;
203     return len;
204   }
205 
206   // Copy all of the available data.
207   // TODO: Replace the for loop with a call to internal memcpy.
208   for (size_t i = 0; i < available_data; ++i)
209     dataref[i] = bufref[i + pos];
210   read_limit = pos = 0; // Reset the pointers.
211   // Update the dataref to reflect that fact that we have already
212   // copied |available_data| into |data|.
213   dataref = cpp::span<uint8_t>(dataref.data() + available_data,
214                                dataref.size() - available_data);
215 
216   size_t to_fetch = len - available_data;
217   if (to_fetch > bufsize) {
218     auto result = platform_read(this, dataref.data(), to_fetch);
219     size_t fetched_size = result.value;
220     if (result.has_error() || fetched_size < to_fetch) {
221       if (!result.has_error())
222         eof = true;
223       else
224         err = true;
225       return {available_data + fetched_size, result.has_error()};
226     }
227     return len;
228   }
229 
230   // Fetch and buffer another buffer worth of data.
231   auto result = platform_read(this, buf, bufsize);
232   size_t fetched_size = result.value;
233   read_limit += fetched_size;
234   size_t transfer_size = fetched_size >= to_fetch ? to_fetch : fetched_size;
235   for (size_t i = 0; i < transfer_size; ++i)
236     dataref[i] = bufref[i];
237   pos += transfer_size;
238   if (result.has_error() || fetched_size < to_fetch) {
239     if (!result.has_error())
240       eof = true;
241     else
242       err = true;
243   }
244   return {transfer_size + available_data, result.error};
245 }
246 
ungetc_unlocked(int c)247 int File::ungetc_unlocked(int c) {
248   // There is no meaning to unget if:
249   // 1. You are trying to push back EOF.
250   // 2. Read operations are not allowed on this file.
251   // 3. The previous operation was a write operation.
252   if (c == EOF || !read_allowed() || (prev_op == FileOp::WRITE))
253     return EOF;
254 
255   cpp::span<uint8_t> bufref(static_cast<uint8_t *>(buf), bufsize);
256   if (read_limit == 0) {
257     // If |read_limit| is zero, it can mean three things:
258     //   a. This file was just created.
259     //   b. The previous operation was a seek operation.
260     //   c. The previous operation was a read operation which emptied
261     //      the buffer.
262     // For all the above cases, we simply write |c| at the beginning
263     // of the buffer and bump |read_limit|. Note that |pos| will also
264     // be zero in this case, so we don't need to adjust it.
265     bufref[0] = static_cast<unsigned char>(c);
266     ++read_limit;
267   } else {
268     // If |read_limit| is non-zero, it means that there is data in the buffer
269     // from a previous read operation. Which would also mean that |pos| is not
270     // zero. So, we decrement |pos| and write |c| in to the buffer at the new
271     // |pos|. If too many ungetc operations are performed without reads, it
272     // can lead to (pos == 0 but read_limit != 0). We will just error out in
273     // such a case.
274     if (pos == 0)
275       return EOF;
276     --pos;
277     bufref[pos] = static_cast<unsigned char>(c);
278   }
279 
280   eof = false; // There is atleast one character that can be read now.
281   err = false; // This operation was a success.
282   return c;
283 }
284 
seek(long offset,int whence)285 ErrorOr<int> File::seek(long offset, int whence) {
286   FileLock lock(this);
287   if (prev_op == FileOp::WRITE && pos > 0) {
288 
289     auto buf_result = platform_write(this, buf, pos);
290     if (buf_result.has_error() || buf_result.value < pos) {
291       err = true;
292       return Error(buf_result.error);
293     }
294   } else if (prev_op == FileOp::READ && whence == SEEK_CUR) {
295     // More data could have been read out from the platform file than was
296     // required. So, we have to adjust the offset we pass to platform seek
297     // function. Note that read_limit >= pos is always true.
298     offset -= (read_limit - pos);
299   }
300   pos = read_limit = 0;
301   prev_op = FileOp::SEEK;
302   // Reset the eof flag as a seek might move the file positon to some place
303   // readable.
304   eof = false;
305   auto result = platform_seek(this, offset, whence);
306   if (!result.has_value())
307     return Error(result.error());
308   else
309     return 0;
310 }
311 
tell()312 ErrorOr<long> File::tell() {
313   FileLock lock(this);
314   auto seek_target = eof ? SEEK_END : SEEK_CUR;
315   auto result = platform_seek(this, 0, seek_target);
316   if (!result.has_value() || result.value() < 0)
317     return Error(result.error());
318   long platform_offset = result.value();
319   if (prev_op == FileOp::READ)
320     return platform_offset - (read_limit - pos);
321   else if (prev_op == FileOp::WRITE)
322     return platform_offset + pos;
323   else
324     return platform_offset;
325 }
326 
flush_unlocked()327 int File::flush_unlocked() {
328   if (prev_op == FileOp::WRITE && pos > 0) {
329     auto buf_result = platform_write(this, buf, pos);
330     if (buf_result.has_error() || buf_result.value < pos) {
331       err = true;
332       return buf_result.error;
333     }
334     pos = 0;
335   }
336   // TODO: Add POSIX behavior for input streams.
337   return 0;
338 }
339 
set_buffer(void * buffer,size_t size,int buffer_mode)340 int File::set_buffer(void *buffer, size_t size, int buffer_mode) {
341   // We do not need to lock the file as this method should be called before
342   // other operations are performed on the file.
343   if (buffer != nullptr && size == 0)
344     return EINVAL;
345 
346   switch (buffer_mode) {
347   case _IOFBF:
348   case _IOLBF:
349   case _IONBF:
350     break;
351   default:
352     return EINVAL;
353   }
354 
355   if (buffer == nullptr && size != 0 && buffer_mode != _IONBF) {
356     // We exclude the case of buffer_mode == _IONBF in this branch
357     // because we don't need to allocate buffer in such a case.
358     if (own_buf) {
359       // This is one of the places where use a C allocation functon
360       // as C++ does not have an equivalent of realloc.
361       buf = reinterpret_cast<uint8_t *>(realloc(buf, size));
362       if (buf == nullptr)
363         return ENOMEM;
364     } else {
365       AllocChecker ac;
366       buf = new (ac) uint8_t[size];
367       if (!ac)
368         return ENOMEM;
369       own_buf = true;
370     }
371     bufsize = size;
372     // TODO: Handle allocation failures.
373   } else {
374     if (own_buf)
375       delete buf;
376     if (buffer_mode != _IONBF) {
377       buf = static_cast<uint8_t *>(buffer);
378       bufsize = size;
379     } else {
380       // We don't need any buffer.
381       buf = nullptr;
382       bufsize = 0;
383     }
384     own_buf = false;
385   }
386   bufmode = buffer_mode;
387   adjust_buf();
388   return 0;
389 }
390 
mode_flags(const char * mode)391 File::ModeFlags File::mode_flags(const char *mode) {
392   // First character in |mode| should be 'a', 'r' or 'w'.
393   if (*mode != 'a' && *mode != 'r' && *mode != 'w')
394     return 0;
395 
396   // There should be exaclty one main mode ('a', 'r' or 'w') character.
397   // If there are more than one main mode characters listed, then
398   // we will consider |mode| as incorrect and return 0;
399   int main_mode_count = 0;
400 
401   ModeFlags flags = 0;
402   for (; *mode != '\0'; ++mode) {
403     switch (*mode) {
404     case 'r':
405       flags |= static_cast<ModeFlags>(OpenMode::READ);
406       ++main_mode_count;
407       break;
408     case 'w':
409       flags |= static_cast<ModeFlags>(OpenMode::WRITE);
410       ++main_mode_count;
411       break;
412     case '+':
413       flags |= static_cast<ModeFlags>(OpenMode::PLUS);
414       break;
415     case 'b':
416       flags |= static_cast<ModeFlags>(ContentType::BINARY);
417       break;
418     case 'a':
419       flags |= static_cast<ModeFlags>(OpenMode::APPEND);
420       ++main_mode_count;
421       break;
422     case 'x':
423       flags |= static_cast<ModeFlags>(CreateType::EXCLUSIVE);
424       break;
425     default:
426       return 0;
427     }
428   }
429 
430   if (main_mode_count != 1)
431     return 0;
432 
433   return flags;
434 }
435 
436 } // namespace LIBC_NAMESPACE
437