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