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