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 
write_unlocked(const void * data,size_t len)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 
write_unlocked_nbf(const void * data,size_t len)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 
write_unlocked_fbf(const void * data,size_t len)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 
write_unlocked_lbf(const void * data,size_t len)130 size_t File::write_unlocked_lbf(const void *data, size_t len) {
131   constexpr char NEWLINE_CHAR = '\n';
132   size_t last_newline = len;
133   for (size_t i = len; i > 1; --i) {
134     if (static_cast<const char *>(data)[i - 1] == NEWLINE_CHAR) {
135       last_newline = i - 1;
136       break;
137     }
138   }
139 
140   // If there is no newline, treat this as fully buffered.
141   if (last_newline == len) {
142     return write_unlocked_fbf(data, len);
143   }
144 
145   // we split |data| (conceptually) using the split point. Then we handle the
146   // two pieces separately.
147   const size_t split_point = last_newline + 1;
148 
149   // The primary piece is everything in |data| up to the newline. It's written
150   // unbuffered to the output.
151   cpp::ArrayRef<uint8_t> primary(data, split_point);
152 
153   // The second piece is the remainder of |data|. It is written fully buffered,
154   // meaning it may stay in the buffer if it fits.
155   cpp::ArrayRef<uint8_t> remainder(
156       static_cast<const uint8_t *>(data) + split_point, len - split_point);
157 
158   size_t written = 0;
159 
160   written = write_unlocked_nbf(primary.data(), primary.size());
161   if (written < primary.size()) {
162     err = true;
163     return written;
164   }
165 
166   flush_unlocked();
167 
168   written += write_unlocked_fbf(remainder.data(), remainder.size());
169   if (written < len) {
170     err = true;
171     return written;
172   }
173 
174   return len;
175 }
176 
read_unlocked(void * data,size_t len)177 size_t File::read_unlocked(void *data, size_t len) {
178   if (!read_allowed()) {
179     errno = EBADF;
180     err = true;
181     return 0;
182   }
183 
184   prev_op = FileOp::READ;
185 
186   cpp::MutableArrayRef<uint8_t> bufref(buf, bufsize);
187   cpp::MutableArrayRef<uint8_t> dataref(data, len);
188 
189   // Because read_limit is always greater than equal to pos,
190   // available_data is never a wrapped around value.
191   size_t available_data = read_limit - pos;
192   if (len <= available_data) {
193     // TODO: Replace the for loop below with a call to internal memcpy.
194     for (size_t i = 0; i < len; ++i)
195       dataref[i] = bufref[i + pos];
196     pos += len;
197     return len;
198   }
199 
200   // Copy all of the available data.
201   // TODO: Replace the for loop with a call to internal memcpy.
202   for (size_t i = 0; i < available_data; ++i)
203     dataref[i] = bufref[i + pos];
204   read_limit = pos = 0; // Reset the pointers.
205 
206   size_t to_fetch = len - available_data;
207   if (to_fetch > bufsize) {
208     size_t fetched_size = platform_read(this, data, to_fetch);
209     if (fetched_size < to_fetch) {
210       if (errno == 0)
211         eof = true;
212       else
213         err = true;
214       return available_data + fetched_size;
215     }
216     return len;
217   }
218 
219   // Fetch and buffer another buffer worth of data.
220   size_t fetched_size = platform_read(this, buf, bufsize);
221   read_limit += fetched_size;
222   size_t transfer_size = fetched_size >= to_fetch ? to_fetch : fetched_size;
223   for (size_t i = 0; i < transfer_size; ++i)
224     dataref[i] = bufref[i];
225   pos += transfer_size;
226   if (fetched_size < to_fetch) {
227     if (errno == 0)
228       eof = true;
229     else
230       err = true;
231   }
232   return transfer_size + available_data;
233 }
234 
seek(long offset,int whence)235 int File::seek(long offset, int whence) {
236   FileLock lock(this);
237   if (prev_op == FileOp::WRITE && pos > 0) {
238     size_t transferred_size = platform_write(this, buf, pos);
239     if (transferred_size < pos) {
240       err = true;
241       return -1;
242     }
243   } else if (prev_op == FileOp::READ && whence == SEEK_CUR) {
244     // More data could have been read out from the platform file than was
245     // required. So, we have to adjust the offset we pass to platform seek
246     // function. Note that read_limit >= pos is always true.
247     offset -= (read_limit - pos);
248   }
249   pos = read_limit = 0;
250   prev_op = FileOp::SEEK;
251   // Reset the eof flag as a seek might move the file positon to some place
252   // readable.
253   eof = false;
254   return platform_seek(this, offset, whence);
255 }
256 
flush_unlocked()257 int File::flush_unlocked() {
258   if (prev_op == FileOp::WRITE && pos > 0) {
259     size_t transferred_size = platform_write(this, buf, pos);
260     if (transferred_size < pos) {
261       err = true;
262       return -1;
263     }
264     pos = 0;
265     return platform_flush(this);
266   }
267   // TODO: Add POSIX behavior for input streams.
268   return 0;
269 }
270 
close()271 int File::close() {
272   {
273     FileLock lock(this);
274     if (prev_op == FileOp::WRITE && pos > 0) {
275       size_t transferred_size = platform_write(this, buf, pos);
276       if (transferred_size < pos) {
277         err = true;
278         return -1;
279       }
280     }
281     if (platform_close(this) != 0)
282       return -1;
283     if (own_buf)
284       free(buf);
285   }
286   free(this);
287   return 0;
288 }
289 
set_buffer(void * buffer,size_t size,bool owned)290 void File::set_buffer(void *buffer, size_t size, bool owned) {
291   if (own_buf)
292     free(buf);
293   buf = buffer;
294   bufsize = size;
295   own_buf = owned;
296 }
297 
mode_flags(const char * mode)298 File::ModeFlags File::mode_flags(const char *mode) {
299   // First character in |mode| should be 'a', 'r' or 'w'.
300   if (*mode != 'a' && *mode != 'r' && *mode != 'w')
301     return 0;
302 
303   // There should be exaclty one main mode ('a', 'r' or 'w') character.
304   // If there are more than one main mode characters listed, then
305   // we will consider |mode| as incorrect and return 0;
306   int main_mode_count = 0;
307 
308   ModeFlags flags = 0;
309   for (; *mode != '\0'; ++mode) {
310     switch (*mode) {
311     case 'r':
312       flags |= static_cast<ModeFlags>(OpenMode::READ);
313       ++main_mode_count;
314       break;
315     case 'w':
316       flags |= static_cast<ModeFlags>(OpenMode::WRITE);
317       ++main_mode_count;
318       break;
319     case '+':
320       flags |= static_cast<ModeFlags>(OpenMode::PLUS);
321       break;
322     case 'b':
323       flags |= static_cast<ModeFlags>(ContentType::BINARY);
324       break;
325     case 'a':
326       flags |= static_cast<ModeFlags>(OpenMode::APPEND);
327       ++main_mode_count;
328       break;
329     case 'x':
330       flags |= static_cast<ModeFlags>(CreateType::EXCLUSIVE);
331       break;
332     default:
333       return 0;
334     }
335   }
336 
337   if (main_mode_count != 1)
338     return 0;
339 
340   return flags;
341 }
342 
343 } // namespace __llvm_libc
344