1 /*
2 * Copyright (c) 2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "fs_stream.h"
17
18 #include <cstring>
19 #include <tuple>
20
21 #include "file_utils.h"
22 #include "filemgmt_libhilog.h"
23
24 namespace OHOS {
25 namespace FileManagement {
26 namespace ModuleFileIO {
27 using namespace std;
28 const string UTF_8 = "utf-8";
29
GetFilePtr()30 std::shared_ptr<FILE> FsStream::GetFilePtr()
31 {
32 std::lock_guard<std::mutex> lock(mtx);
33 if (!streamEntity) {
34 return nullptr;
35 }
36 return streamEntity->fp;
37 }
38
ValidWriteArg(const size_t bufLen,const optional<WriteOptions> & options)39 static tuple<bool, size_t, int64_t> ValidWriteArg(const size_t bufLen, const optional<WriteOptions> &options)
40 {
41 size_t retLen = 0;
42 int64_t offset = -1;
43 bool succ = false;
44
45 if (bufLen > UINT_MAX) {
46 HILOGE("The Size of buffer is too large");
47 return { false, 0, offset };
48 }
49
50 optional<int64_t> lengthOp = nullopt;
51 optional<int64_t> offsetOp = nullopt;
52 optional<string> encodingOp = nullopt;
53 if (options.has_value()) {
54 WriteOptions op = options.value();
55 lengthOp = op.length;
56 offsetOp = op.offset;
57 encodingOp = op.encoding;
58 }
59
60 tie(succ, retLen) = FsUtils::GetActualLen(bufLen, 0, lengthOp);
61 if (!succ) {
62 HILOGE("Failed to get actual length");
63 return { false, 0, offset };
64 }
65
66 if (offsetOp.has_value()) {
67 offset = offsetOp.value();
68 if (offset < 0) {
69 HILOGE("option.offset shall be positive number");
70 return { false, 0, offset };
71 }
72 }
73
74 if (encodingOp.has_value()) {
75 if (encodingOp.value() != UTF_8) {
76 HILOGE("option.encoding shall be utf-8");
77 return { false, 0, offset };
78 }
79 }
80 return { true, retLen, offset };
81 }
82
ValidReadArg(const size_t bufLen,const optional<ReadOptions> & options)83 static tuple<bool, size_t, int64_t> ValidReadArg(const size_t bufLen, const optional<ReadOptions> &options)
84 {
85 size_t retLen = 0;
86 int64_t offset = -1;
87 bool succ = false;
88
89 if (bufLen > UINT_MAX) {
90 HILOGE("The Size of buffer is too large");
91 return { false, 0, offset };
92 }
93
94 optional<int64_t> lengthOp = nullopt;
95 optional<int64_t> offsetOp = nullopt;
96 if (options.has_value()) {
97 ReadOptions op = options.value();
98 lengthOp = op.length;
99 offsetOp = op.offset;
100 }
101
102 tie(succ, retLen) = FsUtils::GetActualLen(bufLen, 0, lengthOp);
103 if (!succ) {
104 HILOGE("Failed to get actual length");
105 return { false, 0, offset };
106 }
107
108 if (offsetOp.has_value()) {
109 offset = offsetOp.value();
110 if (offset < 0) {
111 HILOGE("option.offset shall be positive number");
112 return { false, 0, offset };
113 }
114 }
115 return { true, retLen, offset };
116 }
117
Write(const ArrayBuffer & buf,const optional<WriteOptions> & options)118 FsResult<size_t> FsStream::Write(const ArrayBuffer &buf, const optional<WriteOptions> &options)
119 {
120 auto fp = GetFilePtr();
121 if (!fp) {
122 HILOGE("Failed to get file ptr");
123 return FsResult<size_t>::Error(EIO);
124 }
125
126 auto [succ, retLen, offset] = ValidWriteArg(buf.length, options);
127 if (!succ) {
128 HILOGE("Invalid options");
129 return FsResult<size_t>::Error(EINVAL);
130 }
131
132 if (offset >= 0) {
133 int ret = fseek(fp.get(), static_cast<long>(offset), SEEK_SET);
134 if (ret < 0) {
135 HILOGE("Failed to set the offset location of the file stream pointer, ret: %{public}d", ret);
136 return FsResult<size_t>::Error(errno);
137 }
138 }
139
140 size_t writeLen = fwrite(buf.buf, 1, retLen, fp.get());
141 if ((writeLen == 0) && (writeLen != retLen)) {
142 HILOGE("Failed to fwrite stream");
143 return FsResult<size_t>::Error(EIO);
144 }
145 return FsResult<size_t>::Success(writeLen);
146 }
147
Write(const string & buf,const optional<WriteOptions> & options)148 FsResult<size_t> FsStream::Write(const string &buf, const optional<WriteOptions> &options)
149 {
150 auto fp = GetFilePtr();
151 if (!fp) {
152 HILOGE("Failed to get file ptr");
153 return FsResult<size_t>::Error(EIO);
154 }
155
156 size_t bufLen = static_cast<size_t>(buf.length());
157
158 auto [succ, retLen, offset] = ValidWriteArg(bufLen, options);
159 if (!succ) {
160 HILOGE("Invalid options");
161 return FsResult<size_t>::Error(EINVAL);
162 }
163
164 if (offset >= 0) {
165 int ret = fseek(fp.get(), static_cast<long>(offset), SEEK_SET);
166 if (ret < 0) {
167 HILOGE("Failed to set the offset location of the file stream pointer, ret: %{public}d", ret);
168 return FsResult<size_t>::Error(errno);
169 }
170 }
171
172 size_t writeLen = fwrite(buf.c_str(), 1, retLen, fp.get());
173 if ((writeLen == 0) && (writeLen != retLen)) {
174 HILOGE("Failed to fwrite stream");
175 return FsResult<size_t>::Error(EIO);
176 }
177 return FsResult<size_t>::Success(writeLen);
178 }
179
Read(ArrayBuffer & buf,const optional<ReadOptions> & options)180 FsResult<size_t> FsStream::Read(ArrayBuffer &buf, const optional<ReadOptions> &options)
181 {
182 auto fp = GetFilePtr();
183 if (!fp) {
184 HILOGE("Failed to get file ptr");
185 return FsResult<size_t>::Error(EIO);
186 }
187
188 auto [succ, retLen, offset] = ValidReadArg(buf.length, options);
189 if (!succ) {
190 HILOGE("Invalid options");
191 return FsResult<size_t>::Error(EINVAL);
192 }
193
194 if (offset >= 0) {
195 int ret = fseek(fp.get(), static_cast<long>(offset), SEEK_SET);
196 if (ret < 0) {
197 HILOGE("Failed to set the offset location of the file stream pointer, ret: %{public}d", ret);
198 return FsResult<size_t>::Error(errno);
199 }
200 }
201
202 size_t actLen = fread(buf.buf, 1, retLen, fp.get());
203 if ((actLen != static_cast<size_t>(retLen) && !feof(fp.get())) || ferror(fp.get())) {
204 HILOGE("Invalid buffer size or pointer, actlen: %{public}zu", actLen);
205 return FsResult<size_t>::Error(EIO);
206 }
207
208 return FsResult<size_t>::Success(actLen);
209 }
210
Flush()211 FsResult<void> FsStream::Flush()
212 {
213 auto fp = GetFilePtr();
214 if (fp == nullptr) {
215 HILOGE("Failed to get entity of Stream");
216 return FsResult<void>::Error(EIO);
217 }
218
219 int ret = fflush(fp.get());
220 if (ret < 0) {
221 HILOGE("Failed to fflush file in the stream, ret: %{public}d", ret);
222 return FsResult<void>::Error(errno);
223 }
224
225 return FsResult<void>::Success();
226 }
227
Close()228 FsResult<void> FsStream::Close()
229 {
230 if (!streamEntity) {
231 HILOGE("Failed to get entity of Stream, may closed twice");
232 return FsResult<void>::Error(EIO);
233 }
234 streamEntity = nullptr;
235 return FsResult<void>::Success();
236 }
237
Seek(const int64_t & offset,const optional<int32_t> & typeOpt)238 FsResult<int64_t> FsStream::Seek(const int64_t &offset, const optional<int32_t> &typeOpt)
239 {
240 int whence = SEEK_SET;
241
242 auto fp = GetFilePtr();
243 if (fp == nullptr) {
244 HILOGE("Failed to get file ptr");
245 return FsResult<int64_t>::Error(ENOENT);
246 }
247
248 if (typeOpt.has_value()) {
249 int pos = typeOpt.value();
250 if (pos < SEEK_SET || pos > SEEK_END) {
251 HILOGE("Invalid whence");
252 return FsResult<int64_t>::Error(EINVAL);
253 }
254 whence = pos;
255 }
256
257 if (offset >= 0) {
258 int ret = fseek(fp.get(), static_cast<long>(offset), whence);
259 if (ret < 0) {
260 HILOGE("Failed to set the offset location of the file stream pointer, ret: %{public}d", ret);
261 return FsResult<int64_t>::Error(errno);
262 }
263 }
264
265 int64_t res = ftell(fp.get());
266 if (res < 0) {
267 HILOGE("Failed to tell, error:%{public}d", errno);
268 return FsResult<int64_t>::Error(errno);
269 }
270
271 return FsResult<int64_t>::Success(res);
272 }
273
Constructor()274 FsResult<FsStream *> FsStream::Constructor()
275 {
276 auto rafEntity = CreateUniquePtr<StreamEntity>();
277 if (rafEntity == nullptr) {
278 HILOGE("Failed to request heap memory.");
279 return FsResult<FsStream *>::Error(ENOMEM);
280 }
281 FsStream *fsStreamPtr = new FsStream(move(rafEntity));
282
283 if (fsStreamPtr == nullptr) {
284 HILOGE("Failed to create FsStream object on heap.");
285 return FsResult<FsStream *>::Error(ENOMEM);
286 }
287
288 return FsResult<FsStream *>::Success(move(fsStreamPtr));
289 }
290
291 } // namespace ModuleFileIO
292 } // namespace FileManagement
293 } // namespace OHOS
294