1 /**************************************************************************************************
2 * IOWOW library
3 *
4 * MIT License
5 *
6 * Copyright (c) 2012-2020 Softmotions Ltd <info@softmotions.com>
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in all
16 * copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 * SOFTWARE.
25 *************************************************************************************************/
26
27 #include "iwcfg.h"
28 #include "log/iwlog.h"
29 #include "platform/iwp.h"
30 #include "iwfile.h"
31 #include "iwutils.h"
32
33 #include <fcntl.h>
34 #include <limits.h>
35 #include <unistd.h>
36 #ifdef _WIN32
37 #include <libiberty/libiberty.h>
38 #define strndup xstrndup
39 #endif
40
41 typedef struct IWFS_FILE_IMPL {
42 HANDLE fh; /**< File handle. */
43 iwfs_openstatus ostatus; /**< File open status. */
44 IWFS_FILE_OPTS opts; /**< File open options. */
45 } IWF;
46
_iwfs_write(struct IWFS_FILE * f,off_t off,const void * buf,size_t siz,size_t * sp)47 static iwrc _iwfs_write(struct IWFS_FILE *f, off_t off, const void *buf, size_t siz, size_t *sp) {
48 assert(f);
49 IWF *impl = f->impl;
50 if (!impl) {
51 return IW_ERROR_INVALID_STATE;
52 }
53 if (!(impl->opts.omode & IWFS_OWRITE)) {
54 return IW_ERROR_READONLY;
55 }
56 iwrc rc = iwp_pwrite(impl->fh, off, buf, siz, sp);
57 if (!rc && impl->opts.dlsnr) {
58 rc = impl->opts.dlsnr->onwrite(impl->opts.dlsnr, off, buf, siz, 0);
59 }
60 return rc;
61 }
62
_iwfs_read(struct IWFS_FILE * f,off_t off,void * buf,size_t siz,size_t * sp)63 static iwrc _iwfs_read(struct IWFS_FILE *f, off_t off, void *buf, size_t siz, size_t *sp) {
64 assert(f);
65 IWF *impl = f->impl;
66 if (!impl) {
67 return IW_ERROR_INVALID_STATE;
68 }
69 return iwp_pread(impl->fh, off, buf, siz, sp);
70 }
71
_iwfs_close(struct IWFS_FILE * f)72 static iwrc _iwfs_close(struct IWFS_FILE *f) {
73 if (!f || !f->impl) {
74 return 0;
75 }
76 iwrc rc = 0;
77 IWF *impl = f->impl;
78 IWFS_FILE_OPTS *opts = &impl->opts;
79 #ifndef _WIN32
80 if (opts->path && (opts->omode & IWFS_OUNLINK)) {
81 unlink(opts->path);
82 }
83 #endif
84 if (opts->lock_mode != IWP_NOLOCK) {
85 IWRC(iwp_unlock(impl->fh), rc);
86 }
87 IWRC(iwp_closefh(impl->fh), rc);
88 if (opts->path) {
89 free((char *) opts->path);
90 opts->path = 0;
91 }
92 free(f->impl);
93 f->impl = 0;
94 return rc;
95 }
96
_iwfs_sync(struct IWFS_FILE * f,iwfs_sync_flags flags)97 static iwrc _iwfs_sync(struct IWFS_FILE *f, iwfs_sync_flags flags) {
98 assert(f);
99 iwrc rc = 0;
100 if (!f->impl) {
101 return IW_ERROR_INVALID_STATE;
102 }
103 IWF *impl = f->impl;
104 if (flags & IWFS_FDATASYNC) {
105 #ifdef __APPLE__
106 if (fcntl(impl->fh, F_FULLFSYNC) == -1) {
107 return iwrc_set_errno(IW_ERROR_IO_ERRNO, errno);
108 }
109 #else
110 if (iwp_fdatasync(impl->fh) == -1) {
111 return iwrc_set_errno(IW_ERROR_IO_ERRNO, errno);
112 }
113 #endif
114 } else if (iwp_fsync(impl->fh) == -1) {
115 return iwrc_set_errno(IW_ERROR_IO_ERRNO, errno);
116 }
117 if (impl->opts.dlsnr) {
118 rc = impl->opts.dlsnr->onsynced(impl->opts.dlsnr, 0);
119 }
120 return rc;
121 }
122
_iwfs_state(struct IWFS_FILE * f,IWFS_FILE_STATE * state)123 static iwrc _iwfs_state(struct IWFS_FILE *f, IWFS_FILE_STATE *state) {
124 assert(f);
125 assert(state);
126 memset(state, 0, sizeof(*state));
127 IWF *impl = f->impl;
128 state->is_open = (impl != 0);
129 if (!state->is_open) {
130 return 0;
131 }
132 state->ostatus = impl->ostatus;
133 state->opts = impl->opts;
134 state->fh = impl->fh;
135 return 0;
136 }
137
_iwfs_copy(struct IWFS_FILE * f,off_t off,size_t siz,off_t noff)138 static iwrc _iwfs_copy(struct IWFS_FILE *f, off_t off, size_t siz, off_t noff) {
139 assert(f);
140 IWF *impl = f->impl;
141 if (!impl) {
142 return IW_ERROR_INVALID_STATE;
143 }
144 if (!(impl->opts.omode & IWFS_OWRITE)) {
145 return IW_ERROR_READONLY;
146 }
147 iwrc rc = iwp_copy_bytes(impl->fh, off, siz, noff);
148 if (!rc && impl->opts.dlsnr) {
149 rc = impl->opts.dlsnr->oncopy(impl->opts.dlsnr, off, siz, noff, 0);
150 }
151 return rc;
152 }
153
iwfs_file_open(IWFS_FILE * f,const IWFS_FILE_OPTS * _opts)154 iwrc iwfs_file_open(IWFS_FILE *f, const IWFS_FILE_OPTS *_opts) {
155 if (!f || !_opts || !_opts->path) {
156 return IW_ERROR_INVALID_ARGS;
157 }
158
159 IWFS_FILE_OPTS *opts;
160 IWF *impl;
161 IWP_FILE_STAT fstat;
162 iwfs_omode omode;
163 iwrc rc;
164 int mode;
165
166 memset(f, 0, sizeof(*f));
167 rc = iwfs_file_init();
168 RCRET(rc);
169
170 f->write = _iwfs_write;
171 f->read = _iwfs_read;
172 f->close = _iwfs_close;
173 f->sync = _iwfs_sync;
174 f->state = _iwfs_state;
175 f->copy = _iwfs_copy;
176
177 impl = f->impl = calloc(sizeof(IWF), 1);
178 if (!impl) {
179 return iwrc_set_errno(IW_ERROR_ALLOC, errno);
180 }
181
182 impl->opts = *_opts;
183 opts = &impl->opts;
184
185 if (opts->dlsnr) {
186 IWDLSNR *l = opts->dlsnr;
187 if (!l->onopen || !l->onclosing || !l->oncopy || !l->onresize ||
188 !l->onset || !l->onsynced || !l->onwrite) {
189 iwlog_ecode_error2(IW_ERROR_INVALID_ARGS, "Invalid 'opts->dlsnr' specified");
190 return IW_ERROR_INVALID_ARGS;
191 }
192 }
193
194 if (opts->omode & IWFS_OTMP) {
195 opts->path = iwp_allocate_tmpfile_path(opts->path);
196 if (!opts->path) {
197 rc = iwrc_set_errno(IW_ERROR_ERRNO, errno);
198 goto finish;
199 }
200 } else {
201 opts->path = strndup(_opts->path, MAXPATHLEN);
202 if (!opts->path) {
203 rc = iwrc_set_errno(IW_ERROR_ALLOC, errno);
204 goto finish;
205 }
206 }
207
208 if (!opts->lock_mode) {
209 opts->lock_mode = IWFS_DEFAULT_LOCKMODE; // -V1048
210 }
211 if (!opts->omode) {
212 opts->omode = IWFS_DEFAULT_OMODE;
213 }
214 if (!opts->filemode) {
215 opts->filemode = IWFS_DEFAULT_FILEMODE;
216 }
217 opts->omode |= IWFS_OREAD;
218 if (opts->omode & IWFS_OTMP) {
219 opts->omode |= IWFS_OTRUNC;
220 opts->lock_mode |= IWP_WLOCK;
221 }
222 if (opts->omode & IWFS_OTRUNC) {
223 opts->omode |= IWFS_OWRITE;
224 opts->omode |= IWFS_OCREATE;
225 }
226 if (opts->omode & IWFS_OUNLINK) {
227 opts->omode |= IWFS_OWRITE;
228 }
229 if ((opts->omode & IWFS_OCREATE) || (opts->omode & IWFS_OTRUNC)) {
230 opts->omode |= IWFS_OWRITE;
231 }
232 omode = opts->omode;
233
234 if (!(opts->omode & IWFS_OWRITE) && (opts->lock_mode & IWP_WLOCK)) {
235 opts->lock_mode &= ~IWP_WLOCK;
236 }
237 rc = iwp_fstat(opts->path, &fstat);
238 if (!rc && !(opts->omode & IWFS_OTRUNC)) {
239 impl->ostatus = IWFS_OPEN_EXISTING;
240 } else {
241 impl->ostatus = IWFS_OPEN_NEW;
242 }
243 rc = 0;
244 mode = O_RDONLY;
245 if (omode & IWFS_OWRITE) {
246 mode = O_RDWR;
247 if (omode & IWFS_OCREATE) {
248 mode |= O_CREAT;
249 }
250 if (omode & IWFS_OTRUNC) {
251 mode |= O_TRUNC;
252 }
253 }
254 #ifndef _WIN32
255 impl->fh = open(opts->path, mode, opts->filemode);
256 if (INVALIDHANDLE(impl->fh)) {
257 if (errno == ENOENT) {
258 rc = IW_ERROR_NOT_EXISTS;
259 } else {
260 rc = iwrc_set_errno(IW_ERROR_IO_ERRNO, errno);
261 }
262 goto finish;
263 }
264 #else
265 DWORD womode = GENERIC_READ;
266 DWORD wcmode = OPEN_EXISTING;
267 if (omode & IWFS_OWRITE) {
268 womode |= GENERIC_WRITE;
269 if (omode & IWFS_OTRUNC) {
270 wcmode = CREATE_ALWAYS;
271 } else if (omode & IWFS_OCREATE) {
272 wcmode = OPEN_ALWAYS;
273 }
274 }
275 DWORD smode = FILE_SHARE_READ | FILE_SHARE_WRITE;
276 DWORD flags = FILE_ATTRIBUTE_NORMAL;
277 if (opts->omode & IWFS_OUNLINK) {
278 smode |= FILE_SHARE_DELETE;
279 flags |= FILE_FLAG_DELETE_ON_CLOSE;
280 }
281 impl->fh = CreateFile(opts->path, womode, smode, NULL, wcmode, flags, NULL);
282 if (INVALIDHANDLE(impl->fh)) {
283 uint32_t err = GetLastError();
284 if (err == ERROR_FILE_NOT_FOUND) {
285 rc = IW_ERROR_NOT_EXISTS;
286 } else {
287 rc = iwrc_set_werror(IW_ERROR_IO_ERRNO, err);
288 }
289 goto finish;
290 }
291 #endif
292 if (opts->lock_mode != IWP_NOLOCK) {
293 rc = iwp_flock(impl->fh, opts->lock_mode);
294 RCGO(rc, finish);
295 }
296 finish:
297 if (rc) {
298 impl->ostatus = IWFS_OPEN_FAIL;
299 if (opts->path) {
300 free((char *) opts->path);
301 }
302 f->impl = 0;
303 free(impl);
304 }
305 return rc;
306 }
307
iwfs_file_init(void)308 iwrc iwfs_file_init(void) {
309 return iw_init();
310 }
311