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/ArrayRef.h"
12 
13 #include <errno.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 
17 namespace __llvm_libc {
18 
19 size_t File::write_unlocked(const void *data, size_t len) {
20   if (!write_allowed()) {
21     errno = EBADF;
22     err = true;
23     return 0;
24   }
25 
26   prev_op = FileOp::WRITE;
27 
28   if (bufmode == _IOFBF) { // fully buffered
29     return write_unlocked_fbf(data, len);
30   } else if (bufmode == _IOLBF) { // line buffered
31     return write_unlocked_lbf(data, len);
32   } else /*if (bufmode == _IONBF) */ { // unbuffered
33     size_t ret_val = write_unlocked_nbf(data, len);
34     flush_unlocked();
35     return ret_val;
36   }
37 }
38 
39 size_t File::write_unlocked_nbf(const void *data, size_t len) {
40   if (pos > 0) { // If the buffer is not empty
41     // Flush the buffer
42     const size_t write_size = pos;
43     size_t bytes_written = platform_write(this, buf, write_size);
44     pos = 0; // Buffer is now empty so reset pos to the beginning.
45     // If less bytes were written than expected, then an error occurred.
46     if (bytes_written < write_size) {
47       err = true;
48       return 0; // No bytes from data were written, so return 0.
49     }
50   }
51 
52   size_t written = platform_write(this, data, len);
53   if (written < len)
54     err = true;
55   return written;
56 }
57 
58 size_t File::write_unlocked_fbf(const void *data, size_t len) {
59   const size_t init_pos = pos;
60   const size_t bufspace = bufsize - pos;
61 
62   // If data is too large to be buffered at all, then just write it unbuffered.
63   if (len > bufspace + bufsize)
64     return write_unlocked_nbf(data, len);
65 
66   // we split |data| (conceptually) using the split point. Then we handle the
67   // two pieces separately.
68   const size_t split_point = len < bufspace ? len : bufspace;
69 
70   // The primary piece is the piece of |data| we want to write to the buffer
71   // before flushing. It will always fit into the buffer, since the split point
72   // is defined as being min(len, bufspace), and it will always exist if len is
73   // non-zero.
74   cpp::ArrayRef<uint8_t> primary(data, split_point);
75 
76   // The second piece is the remainder of |data|. It is written to the buffer if
77   // it fits, or written directly to the output if it doesn't. If the primary
78   // piece fits entirely in the buffer, the remainder may be nothing.
79   cpp::ArrayRef<uint8_t> remainder(
80       static_cast<const uint8_t *>(data) + split_point, len - split_point);
81 
82   cpp::MutableArrayRef<uint8_t> bufref(buf, bufsize);
83 
84   // Copy the first piece into the buffer.
85   // TODO: Replace the for loop below with a call to internal memcpy.
86   for (size_t i = 0; i < primary.size(); ++i)
87     bufref[pos + i] = primary[i];
88   pos += primary.size();
89 
90   // If there is no remainder, we can return early, since the first piece has
91   // fit completely into the buffer.
92   if (remainder.size() == 0)
93     return len;
94 
95   // We need to flush the buffer now, since there is still data and the buffer
96   // is full.
97   const size_t write_size = pos;
98   size_t bytes_written = platform_write(this, buf, write_size);
99   pos = 0; // Buffer is now empty so reset pos to the beginning.
100   // If less bytes were written than expected, then an error occurred. Return
101   // the number of bytes that have been written from |data|.
102   if (bytes_written < write_size) {
103     err = true;
104     return bytes_written <= init_pos ? 0 : bytes_written - init_pos;
105   }
106 
107   // The second piece is handled basically the same as the first, although we
108   // know that if the second piece has data in it then the buffer has been
109   // flushed, meaning that pos is always 0.
110   if (remainder.size() < bufsize) {
111     // TODO: Replace the for loop below with a call to internal memcpy.
112     for (size_t i = 0; i < remainder.size(); ++i)
113       bufref[i] = remainder[i];
114     pos = remainder.size();
115   } else {
116     size_t bytes_written =
117         platform_write(this, remainder.data(), remainder.size());
118 
119     // If less bytes were written than expected, then an error occurred. Return
120     // the number of bytes that have been written from |data|.
121     if (bytes_written < remainder.size()) {
122       err = true;
123       return primary.size() + bytes_written;
124     }
125   }
126 
127   return len;
128 }
129 
130 size_t File::write_unlocked_lbf(const void *data, size_t len) {
131   const size_t init_pos = pos;
132   const size_t bufspace = bufsize - pos;
133 
134   constexpr char NEWLINE_CHAR = '\n';
135   size_t last_newline = len;
136   for (size_t i = len - 1; i > 0; --i) {
137     if (static_cast<const char *>(data)[i] == NEWLINE_CHAR) {
138       last_newline = i;
139       break;
140     }
141   }
142 
143   // If there is no newline, treat this as fully buffered.
144   if (last_newline == len) {
145     return write_unlocked_fbf(data, len);
146   }
147 
148   // we split |data| (conceptually) using the split point. Then we handle the
149   // two pieces separately.
150   const size_t split_point = last_newline + 1;
151 
152   // The primary piece is everything in |data| up to the newline. It's written
153   // unbuffered to the output.
154   cpp::ArrayRef<uint8_t> primary(data, split_point);
155 
156   // The second piece is the remainder of |data|. It is written fully buffered,
157   // meaning it may stay in the buffer if it fits.
158   cpp::ArrayRef<uint8_t> remainder(
159       static_cast<const uint8_t *>(data) + split_point, len - split_point);
160 
161   size_t written = 0;
162 
163   written = write_unlocked_nbf(primary.data(), primary.size());
164   if (written < primary.size()) {
165     err = true;
166     return written;
167   }
168 
169   flush_unlocked();
170 
171   written += write_unlocked_fbf(remainder.data(), remainder.size());
172   if (written < primary.size() + remainder.size()) {
173     err = true;
174     return written;
175   }
176 
177   return len;
178 }
179 
180 size_t File::read_unlocked(void *data, size_t len) {
181   if (!read_allowed()) {
182     errno = EBADF;
183     err = true;
184     return 0;
185   }
186 
187   prev_op = FileOp::READ;
188 
189   cpp::MutableArrayRef<uint8_t> bufref(buf, bufsize);
190   cpp::MutableArrayRef<uint8_t> dataref(data, len);
191 
192   // Because read_limit is always greater than equal to pos,
193   // available_data is never a wrapped around value.
194   size_t available_data = read_limit - pos;
195   if (len <= available_data) {
196     // TODO: Replace the for loop below with a call to internal memcpy.
197     for (size_t i = 0; i < len; ++i)
198       dataref[i] = bufref[i + pos];
199     pos += len;
200     return len;
201   }
202 
203   // Copy all of the available data.
204   // TODO: Replace the for loop with a call to internal memcpy.
205   for (size_t i = 0; i < available_data; ++i)
206     dataref[i] = bufref[i + pos];
207   read_limit = pos = 0; // Reset the pointers.
208 
209   size_t to_fetch = len - available_data;
210   if (to_fetch > bufsize) {
211     size_t fetched_size = platform_read(this, data, to_fetch);
212     if (fetched_size < to_fetch) {
213       if (errno == 0)
214         eof = true;
215       else
216         err = true;
217       return available_data + fetched_size;
218     }
219     return len;
220   }
221 
222   // Fetch and buffer another buffer worth of data.
223   size_t fetched_size = platform_read(this, buf, bufsize);
224   read_limit += fetched_size;
225   size_t transfer_size = fetched_size >= to_fetch ? to_fetch : fetched_size;
226   for (size_t i = 0; i < transfer_size; ++i)
227     dataref[i] = bufref[i];
228   pos += transfer_size;
229   if (fetched_size < to_fetch) {
230     if (errno == 0)
231       eof = true;
232     else
233       err = true;
234   }
235   return transfer_size + available_data;
236 }
237 
238 int File::seek(long offset, int whence) {
239   FileLock lock(this);
240   if (prev_op == FileOp::WRITE && pos > 0) {
241     size_t transferred_size = platform_write(this, buf, pos);
242     if (transferred_size < pos) {
243       err = true;
244       return -1;
245     }
246   } else if (prev_op == FileOp::READ && whence == SEEK_CUR) {
247     // More data could have been read out from the platform file than was
248     // required. So, we have to adjust the offset we pass to platform seek
249     // function. Note that read_limit >= pos is always true.
250     offset -= (read_limit - pos);
251   }
252   pos = read_limit = 0;
253   prev_op = FileOp::SEEK;
254   // Reset the eof flag as a seek might move the file positon to some place
255   // readable.
256   eof = false;
257   return platform_seek(this, offset, whence);
258 }
259 
260 int File::flush_unlocked() {
261   if (prev_op == FileOp::WRITE && pos > 0) {
262     size_t transferred_size = platform_write(this, buf, pos);
263     if (transferred_size < pos) {
264       err = true;
265       return -1;
266     }
267     pos = 0;
268     return platform_flush(this);
269   }
270   // TODO: Add POSIX behavior for input streams.
271   return 0;
272 }
273 
274 int File::close() {
275   {
276     FileLock lock(this);
277     if (prev_op == FileOp::WRITE && pos > 0) {
278       size_t transferred_size = platform_write(this, buf, pos);
279       if (transferred_size < pos) {
280         err = true;
281         return -1;
282       }
283     }
284     if (platform_close(this) != 0)
285       return -1;
286     if (own_buf)
287       free(buf);
288   }
289   free(this);
290   return 0;
291 }
292 
293 void File::set_buffer(void *buffer, size_t size, bool owned) {
294   if (own_buf)
295     free(buf);
296   buf = buffer;
297   bufsize = size;
298   own_buf = owned;
299 }
300 
301 File::ModeFlags File::mode_flags(const char *mode) {
302   // First character in |mode| should be 'a', 'r' or 'w'.
303   if (*mode != 'a' && *mode != 'r' && *mode != 'w')
304     return 0;
305 
306   // There should be exaclty one main mode ('a', 'r' or 'w') character.
307   // If there are more than one main mode characters listed, then
308   // we will consider |mode| as incorrect and return 0;
309   int main_mode_count = 0;
310 
311   ModeFlags flags = 0;
312   for (; *mode != '\0'; ++mode) {
313     switch (*mode) {
314     case 'r':
315       flags |= static_cast<ModeFlags>(OpenMode::READ);
316       ++main_mode_count;
317       break;
318     case 'w':
319       flags |= static_cast<ModeFlags>(OpenMode::WRITE);
320       ++main_mode_count;
321       break;
322     case '+':
323       flags |= static_cast<ModeFlags>(OpenMode::PLUS);
324       break;
325     case 'b':
326       flags |= static_cast<ModeFlags>(ContentType::BINARY);
327       break;
328     case 'a':
329       flags |= static_cast<ModeFlags>(OpenMode::APPEND);
330       ++main_mode_count;
331       break;
332     case 'x':
333       flags |= static_cast<ModeFlags>(CreateType::EXCLUSIVE);
334       break;
335     default:
336       return 0;
337     }
338   }
339 
340   if (main_mode_count != 1)
341     return 0;
342 
343   return flags;
344 }
345 
346 } // namespace __llvm_libc
347