1 /* EINA - EFL data type library
2 * Copyright (C) 2013 Cedric Bail
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library;
16 * if not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
22
23 #include <string.h>
24 #include <stdint.h>
25 #include <stdio.h>
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include <errno.h>
29
30 #ifdef _WIN32
31 # include <evil_private.h> /* windows.h fcntl mkstemps mkdtemp */
32 #endif
33
34 #define COPY_BLOCKSIZE (4 * 1024 * 1024)
35
36 #include "eina_config.h"
37 #include "eina_private.h"
38
39 #include "eina_hash.h"
40 #include "eina_safety_checks.h"
41 #include "eina_file_common.h"
42 #include "eina_xattr.h"
43
44 #ifndef O_BINARY
45 # define O_BINARY 0
46 #endif
47
48 #ifdef MAP_FAILED
49 # undef MAP_FAILED
50 #endif
51 #define MAP_FAILED ((void *)-1)
52
53 Eina_Hash *_eina_file_cache = NULL;
54 Eina_Lock _eina_file_lock_cache;
55
56 #if defined(EINA_SAFETY_CHECKS) && defined(EINA_MAGIC_DEBUG)
57 # define EINA_FILE_MAGIC_CHECK(f, ...) do { \
58 if (EINA_UNLIKELY((f) == NULL)) \
59 { \
60 EINA_SAFETY_ERROR("safety check failed: " # f " == NULL"); \
61 return __VA_ARGS__; \
62 } \
63 if (EINA_UNLIKELY((f)->__magic != EINA_FILE_MAGIC)) \
64 { \
65 EINA_MAGIC_FAIL(f, EINA_FILE_MAGIC); \
66 return __VA_ARGS__; \
67 } \
68 } while (0)
69 #else
70 # define EINA_FILE_MAGIC_CHECK(f, ...) do {} while(0)
71 #endif
72
73 static Eina_Spinlock _eina_statgen_lock;
74 static Eina_Statgen _eina_statgen = 0;
75
76 EAPI void
eina_file_statgen_next(void)77 eina_file_statgen_next(void)
78 {
79 eina_spinlock_take(&_eina_statgen_lock);
80 if (_eina_statgen != 0)
81 {
82 _eina_statgen++;
83 if (_eina_statgen == 0) _eina_statgen = 1;
84 }
85 eina_spinlock_release(&_eina_statgen_lock);
86 }
87
88 EAPI Eina_Statgen
eina_file_statgen_get(void)89 eina_file_statgen_get(void)
90 {
91 Eina_Statgen s;
92 eina_spinlock_take(&_eina_statgen_lock);
93 s = _eina_statgen;
94 eina_spinlock_release(&_eina_statgen_lock);
95 return s;
96 }
97
98 EAPI void
eina_file_statgen_enable(void)99 eina_file_statgen_enable(void)
100 {
101 eina_spinlock_take(&_eina_statgen_lock);
102 if (_eina_statgen == 0) _eina_statgen = 1;
103 eina_spinlock_release(&_eina_statgen_lock);
104 }
105
106 EAPI void
eina_file_statgen_disable(void)107 eina_file_statgen_disable(void)
108 {
109 eina_spinlock_take(&_eina_statgen_lock);
110 _eina_statgen = 0;
111 eina_spinlock_release(&_eina_statgen_lock);
112 }
113
114 static char *
_eina_file_escape(char * path,size_t len)115 _eina_file_escape(char *path, size_t len)
116 {
117 char *result;
118 char *p;
119 char *q;
120
121 result = path;
122 p = result;
123 q = result;
124
125 if (!result)
126 return NULL;
127
128 #ifdef _WIN32
129 EVIL_PATH_SEP_WIN32_TO_UNIX(path);
130 #endif
131
132 while ((p = strchr(p, '/')))
133 {
134 // remove double `/'
135 if (p[1] == '/')
136 {
137 memmove(p, p + 1, --len - (p - result));
138 result[len] = '\0';
139 }
140 else
141 if (p[1] == '.'
142 && p[2] == '.')
143 {
144 // remove `/../'
145 if (p[3] == '/')
146 {
147 char tmp;
148
149 len -= p + 3 - q;
150 memmove(q, p + 3, len - (q - result));
151 result[len] = '\0';
152 p = q;
153
154 /* Update q correctly. */
155 tmp = *p;
156 *p = '\0';
157 q = strrchr(result, '/');
158 if (!q) q = result;
159 *p = tmp;
160 }
161 else
162 // remove '/..$'
163 if (p[3] == '\0')
164 {
165 len -= p + 2 - q;
166 result[len] = '\0';
167 break;
168 }
169 else
170 {
171 q = p;
172 ++p;
173 }
174 }
175 else
176 if (p[1] == '.'
177 && p[2] == '/')
178 {
179 // remove '/./'
180 len -= 2;
181 memmove(p, p + 2, len - (p - result));
182 result[len] = '\0';
183 q = p;
184 ++p;
185 }
186 else
187 {
188 q = p;
189 ++p;
190 }
191 }
192
193 return result;
194 }
195
196
197 unsigned int
eina_file_map_key_length(const void * key EINA_UNUSED)198 eina_file_map_key_length(const void *key EINA_UNUSED)
199 {
200 return sizeof (unsigned long int) * 2;
201 }
202
203 int
eina_file_map_key_cmp(const unsigned long long int * key1,int key1_length EINA_UNUSED,const unsigned long long int * key2,int key2_length EINA_UNUSED)204 eina_file_map_key_cmp(const unsigned long long int *key1, int key1_length EINA_UNUSED,
205 const unsigned long long int *key2, int key2_length EINA_UNUSED)
206 {
207 if (key1[0] == key2[0])
208 {
209 if (key1[1] == key2[1]) return 0;
210 if (key1[1] > key2[1]) return 1;
211 return -1;
212 }
213 if (key1[0] > key2[0]) return 1;
214 return -1;
215 }
216
217 int
eina_file_map_key_hash(const unsigned long long int * key,int key_length EINA_UNUSED)218 eina_file_map_key_hash(const unsigned long long int *key, int key_length EINA_UNUSED)
219 {
220 return eina_hash_int64(&key[0], sizeof (unsigned long long int))
221 ^ eina_hash_int64(&key[1], sizeof (unsigned long long int));
222 }
223
224 void *
eina_file_virtual_map_all(Eina_File * file)225 eina_file_virtual_map_all(Eina_File *file)
226 {
227 eina_lock_take(&file->lock);
228 file->global_refcount++;
229 eina_lock_release(&file->lock);
230
231 return file->global_map;
232 }
233
234 void *
eina_file_virtual_map_new(Eina_File * file,unsigned long int offset,unsigned long int length)235 eina_file_virtual_map_new(Eina_File *file,
236 unsigned long int offset, unsigned long int length)
237 {
238 Eina_File_Map *map;
239 unsigned long int key[2];
240
241 // offset and length has already been checked by the caller function
242
243 key[0] = offset;
244 key[1] = length;
245
246 eina_lock_take(&file->lock);
247
248 map = eina_hash_find(file->map, &key);
249 if (!map)
250 {
251 map = malloc(sizeof (Eina_File_Map));
252 if (!map) goto on_error;
253
254 map->map = ((char*) file->global_map) + offset;
255 map->offset = offset;
256 map->length = length;
257 map->refcount = 0;
258
259 eina_hash_add(file->map, &key, map);
260 eina_hash_direct_add(file->rmap, &map->map, map);
261 }
262
263 map->refcount++;
264
265 on_error:
266 eina_lock_release(&file->lock);
267 return map ? map->map : NULL;
268 }
269
270 void
eina_file_virtual_map_free(Eina_File * file,void * map)271 eina_file_virtual_map_free(Eina_File *file, void *map)
272 {
273 Eina_File_Map *em;
274
275 eina_lock_take(&file->lock);
276
277 // map could equal global_map even if length != file->length
278 em = eina_hash_find(file->rmap, &map);
279 if (em)
280 {
281 unsigned long int key[2];
282
283 em->refcount--;
284
285 if (em->refcount > 0) goto on_exit;
286
287 key[0] = em->offset;
288 key[1] = em->length;
289
290 eina_hash_del(file->rmap, &map, em);
291 eina_hash_del(file->map, &key, em);
292 }
293 else
294 {
295 if (file->global_map == map)
296 {
297 file->global_refcount--;
298 }
299 }
300
301 on_exit:
302 eina_lock_release(&file->lock);
303 }
304
305 void
eina_file_common_map_free(Eina_File * file,void * map,void (* free_func)(Eina_File_Map * map))306 eina_file_common_map_free(Eina_File *file, void *map,
307 void (*free_func)(Eina_File_Map *map))
308 {
309 Eina_File_Map *em;
310 unsigned long int key[2];
311 Eina_List *l = NULL;
312 Eina_Bool hashed = EINA_TRUE;
313
314 em = eina_hash_find(file->rmap, &map);
315 if (!em)
316 {
317 EINA_LIST_FOREACH(file->dead_map, l, em)
318 if (em->map == map)
319 {
320 hashed = EINA_FALSE;
321 break ;
322 }
323 if (hashed) return ;
324 }
325
326 em->refcount--;
327
328 if (em->refcount > 0) return ;
329
330 key[0] = em->offset;
331 key[1] = em->length;
332
333 if (hashed)
334 {
335 eina_hash_del(file->rmap, &map, em);
336 eina_hash_del(file->map, &key, em);
337 }
338 else
339 {
340 file->dead_map = eina_list_remove_list(file->dead_map, l);
341 free_func(em);
342 }
343 }
344
345 void
eina_file_flush(Eina_File * file,unsigned long int length)346 eina_file_flush(Eina_File *file, unsigned long int length)
347 {
348 Eina_File_Map *tmp;
349 Eina_Iterator *it;
350 Eina_List *dead_map = NULL;
351 Eina_List *l;
352
353 eina_lock_take(&file->lock);
354
355 // File size changed
356 if (file->global_map != MAP_FAILED)
357 {
358 // Forget global map
359 tmp = malloc(sizeof (Eina_File_Map));
360 if (tmp)
361 {
362 tmp->map = file->global_map;
363 tmp->offset = 0;
364 tmp->length = file->length;
365 tmp->refcount = file->refcount;
366
367 file->dead_map = eina_list_append(file->dead_map, tmp);
368 }
369
370 file->global_map = MAP_FAILED;
371 file->global_refcount = 0;
372 }
373
374 it = eina_hash_iterator_data_new(file->map);
375 EINA_ITERATOR_FOREACH(it, tmp)
376 {
377 // Add out of limit map to dead_map
378 if (tmp->offset + tmp->length > length)
379 dead_map = eina_list_append(dead_map, tmp);
380 }
381 eina_iterator_free(it);
382
383 EINA_LIST_FOREACH(dead_map, l, tmp)
384 {
385 unsigned long int key[2];
386
387 key[0] = tmp->offset;
388 key[1] = tmp->length;
389
390 eina_hash_del(file->rmap, &tmp->map, tmp);
391 eina_hash_del(file->map, &key, tmp);
392 }
393
394 file->dead_map = eina_list_merge(file->dead_map, dead_map);
395
396 eina_lock_release(&file->lock);
397 }
398
399 // Private to this file API
400 static void
_eina_file_map_close(Eina_File_Map * map)401 _eina_file_map_close(Eina_File_Map *map)
402 {
403 free(map);
404 }
405
406 // Global API
407
408 EAPI char *
eina_file_path_sanitize(const char * path)409 eina_file_path_sanitize(const char *path)
410 {
411 Eina_Tmpstr *result = NULL;
412 char *r;
413 size_t len;
414
415 if (!path) return NULL;
416
417 len = strlen(path);
418
419 if (eina_file_path_relative(path))
420 {
421 result = eina_file_current_directory_get(path, len);
422 len = eina_tmpstr_len(result);
423 }
424 else
425 result = path;
426
427 r = _eina_file_escape(strdup(result ? result : ""), len);
428 if (result != path) eina_tmpstr_del(result);
429
430 return r;
431 }
432
433 EAPI Eina_File *
eina_file_virtualize(const char * virtual_name,const void * data,unsigned long long length,Eina_Bool copy)434 eina_file_virtualize(const char *virtual_name, const void *data, unsigned long long length, Eina_Bool copy)
435 {
436 Eina_File *file;
437 Eina_Nano_Time tp;
438 long int ti;
439 const char *tmpname = "/dev/mem/virtual\\/%16x";
440 size_t slen, head_padded;
441
442 EINA_SAFETY_ON_NULL_RETURN_VAL(data, NULL);
443
444 // Generate an almost uniq filename based on current nsec time.
445 if (_eina_time_get(&tp)) return NULL;
446 ti = _eina_time_convert(&tp);
447
448 slen = virtual_name ? strlen(virtual_name) + 1 : strlen(tmpname) + 17;
449 // align copied data at end of file struct to 16 bytes...
450 head_padded = 16 * ((sizeof(Eina_File) + slen + 15) / 16);
451
452 file = malloc(head_padded + (copy ? length : 0));
453 if (!file) return NULL;
454
455 memset(file, 0, sizeof(Eina_File));
456 EINA_MAGIC_SET(file, EINA_FILE_MAGIC);
457 file->filename = (char *)(file + 1);
458 if (virtual_name)
459 file->filename = eina_stringshare_add(virtual_name);
460 else
461 file->filename = eina_stringshare_printf(tmpname, ti);
462
463 eina_lock_recursive_new(&file->lock);
464 file->mtime = ti / 1000;
465 file->length = length;
466 #ifdef _STAT_VER_LINUX
467 file->mtime_nsec = ti;
468 #endif
469 file->refcount = 1;
470 #ifndef _WIN32
471 file->fd = -1;
472 #else
473 file->handle = INVALID_HANDLE_VALUE;
474 #endif
475 file->virtual = EINA_TRUE;
476 file->map = eina_hash_new(EINA_KEY_LENGTH(eina_file_map_key_length),
477 EINA_KEY_CMP(eina_file_map_key_cmp),
478 EINA_KEY_HASH(eina_file_map_key_hash),
479 EINA_FREE_CB(_eina_file_map_close),
480 3);
481 file->rmap = eina_hash_pointer_new(NULL);
482
483 if (copy)
484 {
485 file->copied = EINA_TRUE;
486 file->global_map = ((char *)file) + head_padded;
487 memcpy((char *)file->global_map, data, length);
488 }
489 else
490 {
491 file->global_map = (void *)data;
492 }
493
494 return file;
495 }
496
497 EAPI Eina_Bool
eina_file_virtual(Eina_File * file)498 eina_file_virtual(Eina_File *file)
499 {
500 if (!file) return EINA_FALSE;
501 EINA_FILE_MAGIC_CHECK(file, EINA_FALSE);
502 return file->virtual;
503 }
504
505 EAPI Eina_File *
eina_file_dup(const Eina_File * f)506 eina_file_dup(const Eina_File *f)
507 {
508 Eina_File *file = (Eina_File*) f;
509
510 if (file)
511 {
512 EINA_FILE_MAGIC_CHECK(f, NULL);
513 eina_lock_take(&file->lock);
514
515 // For ease of use and safety of the API, if you dup a virtualized file, we prefer to make a copy
516 if (file->virtual && !file->copied)
517 {
518 Eina_File *r;
519
520 r = eina_file_virtualize(file->filename, file->global_map, file->length, EINA_TRUE);
521 eina_lock_release(&file->lock);
522
523 return r;
524 }
525 file->refcount++;
526 eina_lock_release(&file->lock);
527 }
528 return file;
529 }
530
531 void
eina_file_clean_close(Eina_File * file)532 eina_file_clean_close(Eina_File *file)
533 {
534 // Generic destruction of the file
535 eina_hash_free(file->rmap); file->rmap = NULL;
536 eina_hash_free(file->map); file->map = NULL;
537 eina_stringshare_del(file->filename);
538
539 // Backend specific file resource close
540 eina_file_real_close(file);
541
542 // Final death
543 EINA_MAGIC_SET(file, 0);
544 free(file);
545 }
546
547 EAPI void
eina_file_close(Eina_File * file)548 eina_file_close(Eina_File *file)
549 {
550 Eina_Bool leave = EINA_TRUE;
551 unsigned int key;
552
553 if (!file) return ;
554 EINA_FILE_MAGIC_CHECK(file);
555
556 eina_lock_take(&_eina_file_lock_cache);
557
558 eina_lock_take(&file->lock);
559 file->refcount--;
560 if (file->refcount == 0) leave = EINA_FALSE;
561 eina_lock_release(&file->lock);
562 if (leave) goto end;
563
564 key = eina_hash_superfast((void*) &file->filename, sizeof (void*));
565 if (eina_hash_find_by_hash(_eina_file_cache,
566 file->filename, 0, key) == file)
567 {
568 eina_hash_del_by_key_hash(_eina_file_cache,
569 file->filename, 0, key);
570 }
571
572 eina_file_clean_close(file);
573 end:
574 eina_lock_release(&_eina_file_lock_cache);
575 }
576
577 EAPI size_t
eina_file_size_get(const Eina_File * file)578 eina_file_size_get(const Eina_File *file)
579 {
580 EINA_FILE_MAGIC_CHECK(file, 0);
581 return file->length;
582 }
583
584 EAPI time_t
eina_file_mtime_get(const Eina_File * file)585 eina_file_mtime_get(const Eina_File *file)
586 {
587 EINA_FILE_MAGIC_CHECK(file, 0);
588 return file->mtime;
589 }
590
591 EAPI const char *
eina_file_filename_get(const Eina_File * file)592 eina_file_filename_get(const Eina_File *file)
593 {
594 EINA_FILE_MAGIC_CHECK(file, NULL);
595 return file->filename;
596 }
597
598 Eina_Stringshare *
eina_file_sanitize(const char * path)599 eina_file_sanitize(const char *path)
600 {
601 char *filename;
602 Eina_Stringshare *ss;
603
604 filename = eina_file_path_sanitize(path);
605 if (!filename) return NULL;
606
607 ss = eina_stringshare_add(filename);
608 free(filename);
609 return ss;
610 }
611
612 /* search '\r' and '\n' by preserving cache locality and page locality
613 in doing a search inside 4K boundary.
614 */
615 static inline const char *
_eina_find_eol(const char * start,int boundary,const char * end)616 _eina_find_eol(const char *start, int boundary, const char *end)
617 {
618 const char *cr;
619 const char *lf;
620 unsigned long long chunk;
621
622 while (start < end)
623 {
624 chunk = start + boundary < end ? boundary : end - start;
625 cr = memchr(start, '\r', chunk);
626 lf = memchr(start, '\n', chunk);
627 if (cr)
628 {
629 if (lf && lf < cr)
630 return lf;
631 return cr;
632 }
633 else if (lf)
634 return lf;
635
636 start += chunk;
637 boundary = 4096;
638 }
639
640 return end;
641 }
642
643 static Eina_Bool
_eina_file_map_lines_iterator_next(Eina_Lines_Iterator * it,void ** data)644 _eina_file_map_lines_iterator_next(Eina_Lines_Iterator *it, void **data)
645 {
646 const char *eol;
647 unsigned char match;
648
649 if (it->current.end >= it->end)
650 return EINA_FALSE;
651
652 match = *it->current.end;
653 if (it->current.index > 0)
654 it->current.end++;
655 while (it->current.end < it->end &&
656 (*it->current.end == '\n' || *it->current.end == '\r'))
657 {
658 if (match == *it->current.end)
659 break;
660 it->current.end++;
661 }
662 it->current.index++;
663
664 if (it->current.end == it->end)
665 return EINA_FALSE;
666
667 eol = _eina_find_eol(it->current.end,
668 it->boundary,
669 it->end);
670 it->boundary = (uintptr_t) eol & 0x3FF;
671 if (it->boundary == 0) it->boundary = 4096;
672
673 it->current.start = it->current.end;
674
675 it->current.end = eol;
676 it->current.length = eol - it->current.start;
677
678 *data = &it->current;
679 return EINA_TRUE;
680 }
681
682 static Eina_File *
_eina_file_map_lines_iterator_container(Eina_Lines_Iterator * it)683 _eina_file_map_lines_iterator_container(Eina_Lines_Iterator *it)
684 {
685 return it->fp;
686 }
687
688 static void
_eina_file_map_lines_iterator_free(Eina_Lines_Iterator * it)689 _eina_file_map_lines_iterator_free(Eina_Lines_Iterator *it)
690 {
691 eina_file_map_free(it->fp, (void*) it->map);
692 eina_file_close(it->fp);
693
694 EINA_MAGIC_SET(&it->iterator, 0);
695 free(it);
696 }
697
698 EAPI Eina_Iterator *
eina_file_map_lines(Eina_File * file)699 eina_file_map_lines(Eina_File *file)
700 {
701 Eina_Lines_Iterator *it;
702
703 EINA_FILE_MAGIC_CHECK(file, NULL);
704
705 if (file->length == 0) return NULL;
706
707 it = calloc(1, sizeof (Eina_Lines_Iterator));
708 if (!it) return NULL;
709
710 EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
711
712 it->map = eina_file_map_all(file, EINA_FILE_SEQUENTIAL);
713 if (!it->map)
714 {
715 free(it);
716 return NULL;
717 }
718
719 eina_lock_take(&file->lock);
720 file->refcount++;
721 eina_lock_release(&file->lock);
722
723 it->fp = file;
724 it->boundary = 4096;
725 it->current.start = it->map;
726 it->current.end = it->current.start;
727 it->current.index = 0;
728 it->current.length = 0;
729 it->end = it->map + it->fp->length;
730
731 it->iterator.version = EINA_ITERATOR_VERSION;
732 it->iterator.next = FUNC_ITERATOR_NEXT(_eina_file_map_lines_iterator_next);
733 it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(_eina_file_map_lines_iterator_container);
734 it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_map_lines_iterator_free);
735
736 return &it->iterator;
737 }
738
739 static Eina_Bool
_eina_file_copy_write_internal(int fd,char * buf,size_t size)740 _eina_file_copy_write_internal(int fd, char *buf, size_t size)
741 {
742 size_t done = 0;
743 while (done < size)
744 {
745 ssize_t w = write(fd, buf + done, size - done);
746 if (w >= 0)
747 done += w;
748 else if ((errno != EAGAIN) && (errno != EINTR))
749 {
750 ERR("Error writing destination file during copy: %s",
751 strerror(errno));
752 return EINA_FALSE;
753 }
754 }
755 return EINA_TRUE;
756 }
757
758 static Eina_Bool
_eina_file_copy_read_internal(int fd,char * buf,off_t bufsize,ssize_t * readsize)759 _eina_file_copy_read_internal(int fd, char *buf, off_t bufsize, ssize_t *readsize)
760 {
761 while (1)
762 {
763 ssize_t r = read(fd, buf, bufsize);
764 if (r == 0)
765 {
766 ERR("Premature end of source file during copy.");
767 return EINA_FALSE;
768 }
769 else if (r < 0)
770 {
771 if ((errno != EAGAIN) && (errno != EINTR))
772 {
773 ERR("Error reading source file during copy: %s",
774 strerror(errno));
775 return EINA_FALSE;
776 }
777 }
778 else
779 {
780 *readsize = r;
781 return EINA_TRUE;
782 }
783 }
784 }
785
786 #ifdef HAVE_SPLICE
787 static Eina_Bool
_eina_file_copy_write_splice_internal(int fd,int pipefd,size_t size)788 _eina_file_copy_write_splice_internal(int fd, int pipefd, size_t size)
789 {
790 size_t done = 0;
791 while (done < size)
792 {
793 ssize_t w = splice(pipefd, NULL, fd, NULL, size - done, SPLICE_F_MORE);
794 if (w >= 0)
795 done += w;
796 else if (errno == EINVAL)
797 {
798 INF("Splicing is not supported for destination file");
799 return EINA_FALSE;
800 }
801 else if ((errno != EAGAIN) && (errno != EINTR))
802 {
803 ERR("Error splicing to destination file during copy: %s",
804 strerror(errno));
805 return EINA_FALSE;
806 }
807 }
808 return EINA_TRUE;
809 }
810
811 static Eina_Bool
_eina_file_copy_read_splice_internal(int fd,int pipefd,off_t bufsize,ssize_t * readsize)812 _eina_file_copy_read_splice_internal(int fd, int pipefd, off_t bufsize, ssize_t *readsize)
813 {
814 while (1)
815 {
816 ssize_t r = splice(fd, NULL, pipefd, NULL, bufsize, SPLICE_F_MORE);
817 if (r == 0)
818 {
819 ERR("Premature end of source file during splice.");
820 return EINA_FALSE;
821 }
822 else if (r < 0)
823 {
824 if (errno == EINVAL)
825 {
826 INF("Splicing is not supported for source file");
827 return EINA_FALSE;
828 }
829 else if ((errno != EAGAIN) && (errno != EINTR))
830 {
831 ERR("Error splicing from source file during copy: %s",
832 strerror(errno));
833 return EINA_FALSE;
834 }
835 }
836 else
837 {
838 *readsize = r;
839 return EINA_TRUE;
840 }
841 }
842 }
843 #endif
844
845 static Eina_Bool
_eina_file_copy_splice_internal(int s,int d,off_t total,Eina_File_Copy_Progress cb,const void * cb_data,Eina_Bool * splice_unsupported)846 _eina_file_copy_splice_internal(int s, int d, off_t total, Eina_File_Copy_Progress cb, const void *cb_data, Eina_Bool *splice_unsupported)
847 {
848 #ifdef HAVE_SPLICE
849 off_t bufsize = COPY_BLOCKSIZE;
850 off_t done;
851 Eina_Bool ret;
852 int pipefd[2];
853
854 *splice_unsupported = EINA_TRUE;
855
856 if (pipe(pipefd) < 0) return EINA_FALSE;
857
858 done = 0;
859 ret = EINA_TRUE;
860 while (done < total)
861 {
862 size_t todo;
863 ssize_t r;
864
865 if (done + bufsize < total)
866 todo = bufsize;
867 else
868 todo = total - done;
869
870 ret = _eina_file_copy_read_splice_internal(s, pipefd[1], todo, &r);
871 if (!ret) break;
872
873 ret = _eina_file_copy_write_splice_internal(d, pipefd[0], r);
874 if (!ret) break;
875
876 *splice_unsupported = EINA_FALSE;
877 done += r;
878
879 if (cb)
880 {
881 ret = cb((void *)cb_data, done, total);
882 if (!ret) break;
883 }
884 }
885
886 close(pipefd[0]);
887 close(pipefd[1]);
888
889 return ret;
890 #endif
891 *splice_unsupported = EINA_TRUE;
892 return EINA_FALSE;
893 (void)s;
894 (void)d;
895 (void)total;
896 (void)cb;
897 (void)cb_data;
898 }
899
900 static Eina_Bool
_eina_file_copy_internal(int s,int d,off_t total,Eina_File_Copy_Progress cb,const void * cb_data)901 _eina_file_copy_internal(int s, int d, off_t total, Eina_File_Copy_Progress cb, const void *cb_data)
902 {
903 void *buf = NULL;
904 off_t bufsize = COPY_BLOCKSIZE;
905 off_t done;
906 Eina_Bool ret, splice_unsupported;
907
908 ret = _eina_file_copy_splice_internal(s, d, total, cb, cb_data,
909 &splice_unsupported);
910 if (ret)
911 return EINA_TRUE;
912 else if (!splice_unsupported) /* splice works, but copy failed anyway */
913 return EINA_FALSE;
914
915 /* make sure splice didn't change the position */
916 lseek(s, 0, SEEK_SET);
917 lseek(d, 0, SEEK_SET);
918
919 while ((bufsize > 0) && ((buf = malloc(bufsize)) == NULL))
920 bufsize /= 128;
921
922 EINA_SAFETY_ON_NULL_RETURN_VAL(buf, EINA_FALSE);
923
924 done = 0;
925 ret = EINA_TRUE;
926 while (done < total)
927 {
928 size_t todo;
929 ssize_t r;
930
931 if (done + bufsize < total)
932 todo = bufsize;
933 else
934 todo = total - done;
935
936 ret = _eina_file_copy_read_internal(s, buf, todo, &r);
937 if (!ret) break;
938
939 ret = _eina_file_copy_write_internal(d, buf, r);
940 if (!ret) break;
941
942 done += r;
943
944 if (cb)
945 {
946 ret = cb((void *)cb_data, done, total);
947 if (!ret) break;
948 }
949 }
950
951 free(buf);
952 return ret;
953 }
954
955 EAPI Eina_Bool
eina_file_copy(const char * src,const char * dst,Eina_File_Copy_Flags flags,Eina_File_Copy_Progress cb,const void * cb_data)956 eina_file_copy(const char *src, const char *dst, Eina_File_Copy_Flags flags, Eina_File_Copy_Progress cb, const void *cb_data)
957 {
958 struct stat st;
959 int s, d = -1;
960 Eina_Bool success;
961
962 EINA_SAFETY_ON_NULL_RETURN_VAL(src, EINA_FALSE);
963 EINA_SAFETY_ON_NULL_RETURN_VAL(dst, EINA_FALSE);
964
965 s = open(src, O_RDONLY | O_BINARY);
966 EINA_SAFETY_ON_TRUE_RETURN_VAL (s < 0, EINA_FALSE);
967
968 success = (fstat(s, &st) == 0);
969 EINA_SAFETY_ON_FALSE_GOTO(success, end);
970
971 d = open(dst, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
972 EINA_SAFETY_ON_TRUE_GOTO(d < 0, end);
973
974 success = _eina_file_copy_internal(s, d, st.st_size, cb, cb_data);
975 if (success)
976 {
977 #ifdef HAVE_FCHMOD
978 if (flags & EINA_FILE_COPY_PERMISSION)
979 fchmod(d, st.st_mode);
980 #endif
981 if (flags & EINA_FILE_COPY_XATTR)
982 eina_xattr_fd_copy(s, d);
983 }
984
985 end:
986 if (d >= 0) close(d);
987 else success = EINA_FALSE;
988 close(s);
989
990 if (!success)
991 unlink(dst);
992
993 return success;
994 }
995
996 EAPI int
eina_file_mkstemp(const char * templatename,Eina_Tmpstr ** path)997 eina_file_mkstemp(const char *templatename, Eina_Tmpstr **path)
998 {
999 char buffer[PATH_MAX];
1000 const char *XXXXXX = NULL, *sep;
1001 int fd, len;
1002 #ifndef _WIN32
1003 mode_t old_umask;
1004 #endif
1005
1006 EINA_SAFETY_ON_NULL_RETURN_VAL(templatename, -1);
1007
1008 sep = strchr(templatename, '/');
1009 #ifdef _WIN32
1010 if (!sep) sep = strchr(templatename, '\\');
1011 #endif
1012 if (sep)
1013 {
1014 len = eina_strlcpy(buffer, templatename, sizeof(buffer));
1015 }
1016 else
1017 {
1018 len = eina_file_path_join(buffer, sizeof(buffer),
1019 eina_environment_tmp_get(), templatename);
1020 }
1021
1022 /*
1023 * Unix:
1024 * Make sure temp file is created with secure permissions,
1025 * http://man7.org/linux/man-pages/man3/mkstemp.3.html#NOTES
1026 *
1027 * Windows:
1028 * no secure permissions anyway and the umask use below makes
1029 * the file read-only.
1030 */
1031 #ifndef _WIN32
1032 old_umask = umask(S_IRWXG|S_IRWXO);
1033 #endif
1034 if ((XXXXXX = strstr(buffer, "XXXXXX.")) != NULL)
1035 {
1036 int suffixlen = buffer + len - XXXXXX - 6;
1037 fd = mkstemps(buffer, suffixlen);
1038 }
1039 else
1040 fd = mkstemp(buffer);
1041 #ifndef _WIN32
1042 umask(old_umask);
1043 #endif
1044
1045 if (fd < 0)
1046 {
1047 if (path) *path = NULL;
1048 return -1;
1049 }
1050
1051 if (path) *path = eina_tmpstr_add(buffer);
1052 return fd;
1053 }
1054
1055 EAPI Eina_Bool
eina_file_mkdtemp(const char * templatename,Eina_Tmpstr ** path)1056 eina_file_mkdtemp(const char *templatename, Eina_Tmpstr **path)
1057 {
1058 char buffer[PATH_MAX];
1059 char *tmpdirname, *sep;
1060
1061 EINA_SAFETY_ON_NULL_RETURN_VAL(templatename, EINA_FALSE);
1062
1063 sep = strchr(templatename, '/');
1064 #ifdef _WIN32
1065 if (!sep) sep = strchr(templatename, '\\');
1066 #endif
1067 if (sep)
1068 {
1069 eina_strlcpy(buffer, templatename, sizeof(buffer));
1070 }
1071 else
1072 {
1073 eina_file_path_join(buffer, sizeof(buffer),
1074 eina_environment_tmp_get(), templatename);
1075 }
1076
1077 tmpdirname = mkdtemp(buffer);
1078 if (tmpdirname == NULL)
1079 {
1080 if (path) *path = NULL;
1081 return EINA_FALSE;
1082 }
1083
1084 if (path) *path = eina_tmpstr_add(tmpdirname);
1085 return EINA_TRUE;
1086 }
1087
1088 /*============================================================================*
1089 * Global *
1090 *============================================================================*/
1091
1092 Eina_Bool
eina_file_init(void)1093 eina_file_init(void)
1094 {
1095 _eina_file_log_dom = eina_log_domain_register("eina_file",
1096 EINA_LOG_COLOR_DEFAULT);
1097 if (_eina_file_log_dom < 0)
1098 {
1099 EINA_LOG_ERR("Could not register log domain: eina_file");
1100 return EINA_FALSE;
1101 }
1102
1103 _eina_file_cache = eina_hash_stringshared_new(NULL);
1104 if (!_eina_file_cache)
1105 {
1106 ERR("Could not create cache.");
1107 eina_log_domain_unregister(_eina_file_log_dom);
1108 _eina_file_log_dom = -1;
1109 return EINA_FALSE;
1110 }
1111
1112 eina_spinlock_new(&_eina_statgen_lock);
1113 eina_spinlock_take(&_eina_statgen_lock);
1114 if (getenv("EINA_STATGEN")) _eina_statgen = 1;
1115 eina_spinlock_release(&_eina_statgen_lock);
1116 eina_lock_recursive_new(&_eina_file_lock_cache);
1117 eina_magic_string_set(EINA_FILE_MAGIC, "Eina_File");
1118
1119 return EINA_TRUE;
1120 }
1121
1122 Eina_Bool
eina_file_shutdown(void)1123 eina_file_shutdown(void)
1124 {
1125 if (eina_hash_population(_eina_file_cache) > 0)
1126 {
1127 Eina_Iterator *it;
1128 const char *key;
1129
1130 it = eina_hash_iterator_key_new(_eina_file_cache);
1131 EINA_ITERATOR_FOREACH(it, key)
1132 {
1133 Eina_File *f = eina_hash_find(_eina_file_cache, key);
1134 ERR("File [%s] still open %i times !", key, f->refcount);
1135 }
1136 eina_iterator_free(it);
1137 }
1138
1139 eina_hash_free(_eina_file_cache);
1140 _eina_file_cache = NULL;
1141
1142 eina_lock_free(&_eina_file_lock_cache);
1143
1144 eina_log_domain_unregister(_eina_file_log_dom);
1145 _eina_file_log_dom = -1;
1146 eina_spinlock_free(&_eina_statgen_lock);
1147 return EINA_TRUE;
1148 }
1149
1150 EAPI Eina_Bool
eina_file_close_on_exec(int fd,Eina_Bool on)1151 eina_file_close_on_exec(int fd, Eina_Bool on)
1152 {
1153 #ifdef _WIN32
1154 return EINA_TRUE;
1155 (void)fd;
1156 (void)on;
1157 #elif HAVE_FCNTL
1158 int flags;
1159
1160 flags = fcntl(fd, F_GETFD);
1161 if (flags < 0)
1162 {
1163 int errno_backup = errno;
1164 ERR("%#x = fcntl(%d, F_GETFD): %s", flags, fd, strerror(errno));
1165 errno = errno_backup;
1166 return EINA_FALSE;
1167 }
1168
1169 if (on)
1170 flags |= FD_CLOEXEC;
1171 else
1172 flags &= (~flags);
1173
1174 if (fcntl(fd, F_SETFD, flags) == -1)
1175 {
1176 int errno_backup = errno;
1177 ERR("fcntl(%d, F_SETFD, %#x): %s", fd, flags, strerror(errno));
1178 errno = errno_backup;
1179 return EINA_FALSE;
1180 }
1181 return EINA_TRUE;
1182 #else
1183 static Eina_Bool statement = EINA_FALSE;
1184
1185 if (!statement)
1186 ERR("fcntl is not available on your platform. fd may leak when using exec.");
1187 statement = EINA_TRUE;
1188 return EINA_TRUE;
1189 #endif
1190 }
1191