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(const void *data, size_t len) {
20   FileLock lock(this);
21 
22   if (!write_allowed()) {
23     errno = EBADF;
24     err = true;
25     return 0;
26   }
27 
28   prev_op = FileOp::WRITE;
29 
30   cpp::ArrayRef<uint8_t> dataref(data, len);
31   cpp::MutableArrayRef<uint8_t> bufref(buf, bufsize);
32 
33   const size_t used = pos;
34   const size_t bufspace = bufsize - pos;
35   const size_t write_size = bufspace > len ? len : bufspace;
36   // TODO: Replace the for loop below with a call to internal memcpy.
37   for (size_t i = 0; i < write_size; ++i)
38     bufref[pos + i] = dataref[i];
39   pos += write_size;
40   if (len < bufspace)
41     return len;
42 
43   // If the control reaches beyond this point, it means that |data|
44   // is more than what can be accomodated in the buffer. So, we first
45   // flush out the buffer.
46   size_t bytes_written = platform_write(this, buf, bufsize);
47   pos = 0; // Buffer is now empty so reset pos to the beginning.
48   if (bytes_written < bufsize) {
49     err = true;
50     // If less bytes were written than expected, then there are two
51     // possibilities.
52     // 1. None of the bytes from |data| were flushed out.
53     if (bytes_written <= used)
54       return 0;
55     // 2. Some of the bytes from |data| were written
56     return bytes_written - used;
57   }
58 
59   // If the remaining bytes from |data| can fit in the buffer, write
60   // into it. Else, write it directly to the platform stream.
61   size_t remaining = len - write_size;
62   if (remaining <= len) {
63     // TODO: Replace the for loop below with a call to internal memcpy.
64     for (size_t i = 0; i < remaining; ++i)
65       bufref[i] = dataref[i];
66     pos += remaining;
67     return len;
68   }
69 
70   size_t transferred =
71       platform_write(this, dataref.data() + write_size, remaining);
72   if (transferred < remaining) {
73     err = true;
74     return write_size + transferred;
75   }
76   return len;
77 }
78 
79 size_t File::read(void *data, size_t len) {
80   FileLock lock(this);
81 
82   if (!read_allowed()) {
83     errno = EBADF;
84     err = true;
85     return 0;
86   }
87 
88   prev_op = FileOp::READ;
89 
90   cpp::MutableArrayRef<uint8_t> bufref(buf, bufsize);
91   cpp::MutableArrayRef<uint8_t> dataref(data, len);
92 
93   // Because read_limit is always greater than equal to pos,
94   // available_data is never a wrapped around value.
95   size_t available_data = read_limit - pos;
96   if (len <= available_data) {
97     // TODO: Replace the for loop below with a call to internal memcpy.
98     for (size_t i = 0; i < len; ++i)
99       dataref[i] = bufref[i + pos];
100     pos += len;
101     return len;
102   }
103 
104   // Copy all of the available data.
105   // TODO: Replace the for loop with a call to internal memcpy.
106   for (size_t i = 0; i < available_data; ++i)
107     dataref[i] = bufref[i + pos];
108   read_limit = pos = 0; // Reset the pointers.
109 
110   size_t to_fetch = len - available_data;
111   if (to_fetch > bufsize) {
112     size_t fetched_size = platform_read(this, data, to_fetch);
113     if (fetched_size < to_fetch) {
114       if (errno == 0)
115         eof = true;
116       else
117         err = true;
118       return available_data + fetched_size;
119     }
120     return len;
121   }
122 
123   // Fetch and buffer another buffer worth of data.
124   size_t fetched_size = platform_read(this, buf, bufsize);
125   read_limit += fetched_size;
126   size_t transfer_size = fetched_size >= to_fetch ? to_fetch : fetched_size;
127   for (size_t i = 0; i < transfer_size; ++i)
128     dataref[i] = bufref[i];
129   pos += transfer_size;
130   if (fetched_size < to_fetch) {
131     if (errno == 0)
132       eof = true;
133     else
134       err = true;
135   }
136   return transfer_size + available_data;
137 }
138 
139 int File::seek(long offset, int whence) {
140   FileLock lock(this);
141   if (prev_op == FileOp::WRITE && pos > 0) {
142     size_t transferred_size = platform_write(this, buf, pos);
143     if (transferred_size < pos) {
144       err = true;
145       return -1;
146     }
147   } else if (prev_op == FileOp::READ && whence == SEEK_CUR) {
148     // More data could have been read out from the platform file than was
149     // required. So, we have to adjust the offset we pass to platform seek
150     // function. Note that read_limit >= pos is always true.
151     offset -= (read_limit - pos);
152   }
153   pos = read_limit = 0;
154   prev_op = FileOp::SEEK;
155   // Reset the eof flag as a seek might move the file positon to some place
156   // readable.
157   eof = false;
158   return platform_seek(this, offset, whence);
159 }
160 
161 int File::flush() {
162   FileLock lock(this);
163   if (prev_op == FileOp::WRITE && pos > 0) {
164     size_t transferred_size = platform_write(this, buf, pos);
165     if (transferred_size < pos) {
166       err = true;
167       return -1;
168     }
169     pos = 0;
170     return platform_flush(this);
171   }
172   return 0;
173 }
174 
175 int File::close() {
176   {
177     FileLock lock(this);
178     if (prev_op == FileOp::WRITE && pos > 0) {
179       size_t transferred_size = platform_write(this, buf, pos);
180       if (transferred_size < pos) {
181         err = true;
182         return -1;
183       }
184     }
185     if (platform_close(this) != 0)
186       return -1;
187     if (own_buf)
188       free(buf);
189   }
190   free(this);
191   return 0;
192 }
193 
194 void File::set_buffer(void *buffer, size_t size, bool owned) {
195   if (own_buf)
196     free(buf);
197   buf = buffer;
198   bufsize = size;
199   own_buf = owned;
200 }
201 
202 File::ModeFlags File::mode_flags(const char *mode) {
203   // First character in |mode| should be 'a', 'r' or 'w'.
204   if (*mode != 'a' && *mode != 'r' && *mode != 'w')
205     return 0;
206 
207   // There should be exaclty one main mode ('a', 'r' or 'w') character.
208   // If there are more than one main mode characters listed, then
209   // we will consider |mode| as incorrect and return 0;
210   int main_mode_count = 0;
211 
212   ModeFlags flags = 0;
213   for (; *mode != '\0'; ++mode) {
214     switch (*mode) {
215     case 'r':
216       flags |= static_cast<ModeFlags>(OpenMode::READ);
217       ++main_mode_count;
218       break;
219     case 'w':
220       flags |= static_cast<ModeFlags>(OpenMode::WRITE);
221       ++main_mode_count;
222       break;
223     case '+':
224       flags |= static_cast<ModeFlags>(OpenMode::PLUS);
225       break;
226     case 'b':
227       flags |= static_cast<ModeFlags>(ContentType::BINARY);
228       break;
229     case 'a':
230       flags |= static_cast<ModeFlags>(OpenMode::APPEND);
231       ++main_mode_count;
232       break;
233     case 'x':
234       flags |= static_cast<ModeFlags>(CreateType::EXCLUSIVE);
235       break;
236     default:
237       return 0;
238     }
239   }
240 
241   if (main_mode_count != 1)
242     return 0;
243 
244   return flags;
245 }
246 
247 } // namespace __llvm_libc
248