sparsefile: drop bogus reference to file descriptor
[platform/upstream/gstreamer.git] / plugins / elements / gstsparsefile.c
1 /* GStreamer
2  * Copyright (C) 2014 Wim Taymans <wtaymans@redhat.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 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  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include <gst/gst.h>
25 #include <glib/gstdio.h>
26
27 #include "gstsparsefile.h"
28
29 #ifdef G_OS_WIN32
30 #include <io.h>                 /* lseek, open, close, read */
31 #undef lseek
32 #define lseek _lseeki64
33 #undef off_t
34 #define off_t guint64
35 #else
36 #include <unistd.h>
37 #endif
38
39 #ifdef __BIONIC__               /* Android */
40 #undef lseek
41 #define lseek lseek64
42 #undef off_t
43 #define off_t guint64
44 #endif
45
46 #ifdef HAVE_FSEEKO
47 #define FSEEK_FILE(file,offset)  (fseeko (file, (off_t) offset, SEEK_SET) != 0)
48 #elif defined (G_OS_UNIX) || defined (G_OS_WIN32)
49 #define FSEEK_FILE(file,offset)  (lseek (fileno (file), (off_t) offset, SEEK_SET) == (off_t) -1)
50 #else
51 #define FSEEK_FILE(file,offset)  (fseek (file, offset, SEEK_SET) != 0)
52 #endif
53
54 #define GST_SPARSE_FILE_IO_ERROR \
55     g_quark_from_static_string("gst-sparse-file-io-error-quark")
56
57 static GstSparseFileIOErrorEnum
58 gst_sparse_file_io_error_from_errno (gint err_no);
59
60 typedef struct _GstSparseRange GstSparseRange;
61
62 struct _GstSparseRange
63 {
64   GstSparseRange *next;
65
66   gsize start;
67   gsize stop;
68 };
69
70 #define RANGE_CONTAINS(r,o) ((r)->start <= (o) && (r)->stop > (o))
71
72 struct _GstSparseFile
73 {
74   gint fd;
75   FILE *file;
76   gsize current_pos;
77
78   GstSparseRange *ranges;
79   guint n_ranges;
80
81   GstSparseRange *write_range;
82   GstSparseRange *read_range;
83 };
84
85 static GstSparseRange *
86 get_write_range (GstSparseFile * file, gsize offset)
87 {
88   GstSparseRange *next, *prev, *result = NULL;
89
90   if (file->write_range && file->write_range->stop == offset)
91     return file->write_range;
92
93   prev = NULL;
94   next = file->ranges;
95   while (next) {
96     if (next->start > offset)
97       break;
98
99     if (next->stop >= offset) {
100       result = next;
101       break;
102     }
103     prev = next;
104     next = next->next;
105   }
106   if (result == NULL) {
107     result = g_slice_new0 (GstSparseRange);
108     result->start = offset;
109     result->stop = offset;
110
111     result->next = next;
112     if (prev)
113       prev->next = result;
114     else
115       file->ranges = result;
116
117     file->write_range = result;
118     file->read_range = NULL;
119
120     file->n_ranges++;
121   }
122   return result;
123 }
124
125 static GstSparseRange *
126 get_read_range (GstSparseFile * file, gsize offset, gsize count)
127 {
128   GstSparseRange *walk, *result = NULL;
129
130   if (file->read_range && RANGE_CONTAINS (file->read_range, offset))
131     return file->read_range;
132
133   for (walk = file->ranges; walk; walk = walk->next) {
134     if (walk->start > offset)
135       break;
136
137     if (walk->stop >= offset + count) {
138       result = walk;
139       break;
140     }
141   }
142   return result;
143 }
144
145 /**
146  * gst_sparse_file_new:
147  *
148  * Make a new #GstSparseFile
149  *
150  * Returns: a new #GstSparseFile, gst_sparse_file_free() after usage.
151  *
152  * Since: 1.4
153  */
154 GstSparseFile *
155 gst_sparse_file_new (void)
156 {
157   GstSparseFile *result;
158
159   result = g_slice_new0 (GstSparseFile);
160   result->current_pos = 0;
161   result->ranges = NULL;
162   result->n_ranges = 0;
163
164   return result;
165 }
166
167 /**
168  * gst_sparse_file_set_fd:
169  * @file: a #GstSparseFile
170  * @fd: a file descriptor
171  *
172  * Store the data for @file in the file represented with @fd.
173  *
174  * Returns: %TRUE when @fd could be set
175  *
176  * Since: 1.4
177  */
178 gboolean
179 gst_sparse_file_set_fd (GstSparseFile * file, gint fd)
180 {
181   g_return_val_if_fail (file != NULL, FALSE);
182   g_return_val_if_fail (fd != 0, FALSE);
183
184   file->file = fdopen (fd, "wb+");
185   file->fd = fd;
186
187   return file->file != NULL;
188 }
189
190 /**
191  * gst_sparse_file_clear:
192  * @file: a #GstSparseFile
193  *
194  * Clear all the ranges in @file.
195  */
196 void
197 gst_sparse_file_clear (GstSparseFile * file)
198 {
199   g_return_if_fail (file != NULL);
200
201   if (file->file) {
202     fclose (file->file);
203     file->file = fdopen (file->fd, "wb+");
204   }
205   g_slice_free_chain (GstSparseRange, file->ranges, next);
206   file->current_pos = 0;
207   file->ranges = NULL;
208   file->n_ranges = 0;
209 }
210
211 /**
212  * gst_sparse_file_free:
213  * @file: a #GstSparseFile
214  *
215  * Free the memory used by @file.
216  *
217  * Since: 1.4
218  */
219 void
220 gst_sparse_file_free (GstSparseFile * file)
221 {
222   g_return_if_fail (file != NULL);
223
224   if (file->file) {
225     fflush (file->file);
226     fclose (file->file);
227   }
228   g_slice_free_chain (GstSparseRange, file->ranges, next);
229   g_slice_free (GstSparseFile, file);
230 }
231
232 /**
233  * gst_sparse_file_write:
234  * @file: a #GstSparseFile
235  * @offset: the offset
236  * @data: the data
237  * @count: amount of bytes
238  * @available: amount of bytes already available
239  * @error: a #GError
240  *
241  * Write @count bytes from @data to @file at @offset.
242  *
243  * If @available is not %NULL, it will be updated with the amount of
244  * data already available after the last written byte.
245  *
246  * Returns: The number of bytes written or 0 on error.
247  *
248  * Since: 1.4
249  */
250 gsize
251 gst_sparse_file_write (GstSparseFile * file, gsize offset, gconstpointer data,
252     gsize count, gsize * available, GError ** error)
253 {
254   GstSparseRange *range, *next;
255   gsize stop;
256
257   g_return_val_if_fail (file != NULL, 0);
258   g_return_val_if_fail (count != 0, 0);
259
260   if (file->file) {
261     if (file->current_pos != offset) {
262       GST_DEBUG ("seeking to %" G_GSIZE_FORMAT, offset);
263       if (FSEEK_FILE (file->file, offset))
264         goto error;
265     }
266     if (fwrite (data, count, 1, file->file) != 1)
267       goto error;
268   }
269
270   file->current_pos = offset + count;
271
272   /* update the new stop position in the range */
273   range = get_write_range (file, offset);
274   stop = offset + count;
275   range->stop = MAX (range->stop, stop);
276
277   /* see if we can merge with next region */
278   while ((next = range->next)) {
279     if (next->start > range->stop)
280       break;
281
282     GST_DEBUG ("merging range %" G_GSIZE_FORMAT "-%" G_GSIZE_FORMAT ", next %"
283         G_GSIZE_FORMAT "-%" G_GSIZE_FORMAT, range->start, range->stop,
284         next->start, next->stop);
285
286     range->stop = MAX (next->stop, range->stop);
287     range->next = next->next;
288
289     if (file->write_range == next)
290       file->write_range = NULL;
291     if (file->read_range == next)
292       file->read_range = NULL;
293     g_slice_free (GstSparseRange, next);
294     file->n_ranges--;
295   }
296   if (available)
297     *available = range->stop - stop;
298
299   return count;
300
301   /* ERRORS */
302 error:
303   {
304     g_set_error (error, GST_SPARSE_FILE_IO_ERROR,
305         gst_sparse_file_io_error_from_errno (errno), "Error writing file: %s",
306         g_strerror (errno));
307     return 0;
308   }
309 }
310
311 /**
312  * gst_sparse_file_read:
313  * @file: a #GstSparseFile
314  * @offset: the offset
315  * @data: the data
316  * @count: amount of bytes
317  * @remaining: amount of bytes remaining
318  * @error: a #GError
319  *
320  * Read @count bytes from @file at @offset into @data.
321  *
322  * On error, @error will be set. If there are no @count bytes available
323  * at @offset, %GST_SPARSE_FILE_IO_ERROR_WOULD_BLOCK is returned.
324  *
325  * @remaining will be set to the amount of bytes remaining in the read
326  * range.
327  *
328  * Returns: The number of bytes read of 0 on error.
329  *
330  * Since: 1.4
331  */
332 gsize
333 gst_sparse_file_read (GstSparseFile * file, gsize offset, gpointer data,
334     gsize count, gsize * remaining, GError ** error)
335 {
336   GstSparseRange *range;
337   gsize res = 0;
338
339   g_return_val_if_fail (file != NULL, 0);
340   g_return_val_if_fail (count != 0, 0);
341
342   if ((range = get_read_range (file, offset, count)) == NULL)
343     goto no_range;
344
345   if (file->file) {
346     if (file->current_pos != offset) {
347       GST_DEBUG ("seeking from %" G_GSIZE_FORMAT " to %" G_GSIZE_FORMAT,
348           file->current_pos, offset);
349       if (FSEEK_FILE (file->file, offset))
350         goto error;
351     }
352     res = fread (data, 1, count, file->file);
353     if (G_UNLIKELY (res < count))
354       goto error;
355   }
356
357   file->current_pos = offset + res;
358
359   if (remaining)
360     *remaining = range->stop - file->current_pos;
361
362   return count;
363
364   /* ERRORS */
365 no_range:
366   {
367     g_set_error_literal (error, GST_SPARSE_FILE_IO_ERROR,
368         GST_SPARSE_FILE_IO_ERROR_WOULD_BLOCK, "Offset not written to file yet");
369     return 0;
370   }
371 error:
372   {
373     if (ferror (file->file)) {
374       g_set_error (error, GST_SPARSE_FILE_IO_ERROR,
375           gst_sparse_file_io_error_from_errno (errno), "Error reading file: %s",
376           g_strerror (errno));
377     } else if (feof (file->file)) {
378       return res;
379     }
380     return 0;
381   }
382 }
383
384 /**
385  * gst_sparse_file_n_ranges:
386  * @file: a #GstSparseFile
387  *
388  * Get the number of ranges that are written in @file.
389  *
390  * Returns: the number of written ranges.
391  *
392  * Since: 1.4
393  */
394 guint
395 gst_sparse_file_n_ranges (GstSparseFile * file)
396 {
397   g_return_val_if_fail (file != NULL, 0);
398
399   return file->n_ranges;
400 }
401
402 /**
403  * gst_sparse_file_get_range_before:
404  * @file: a #GstSparseFile
405  * @offset: the range offset
406  * @start: result start
407  * @stop: result stop
408  *
409  * Get the start and stop offset of the range containing data before or
410  * including @offset.
411  *
412  * Returns: %TRUE if the range with data before @offset exists.
413  *
414  * Since: 1.4
415  */
416 gboolean
417 gst_sparse_file_get_range_before (GstSparseFile * file, gsize offset,
418     gsize * start, gsize * stop)
419 {
420   GstSparseRange *walk, *result = NULL;
421
422   g_return_val_if_fail (file != NULL, FALSE);
423
424   for (walk = file->ranges; walk; walk = walk->next) {
425     GST_DEBUG ("start %" G_GSIZE_FORMAT " > %" G_GSIZE_FORMAT,
426         walk->stop, offset);
427     if (walk->start > offset)
428       break;
429
430     if (walk->start <= offset)
431       result = walk;
432   }
433
434   if (result) {
435     if (start)
436       *start = result->start;
437     if (stop)
438       *stop = result->stop;
439   }
440   return result != NULL;
441 }
442
443 /**
444  * gst_sparse_file_get_range_after:
445  * @file: a #GstSparseFile
446  * @offset: the range offset
447  * @start: result start
448  * @stop: result stop
449  *
450  * Get the start and stop offset of the range containing data after or
451  * including @offset.
452  *
453  * Returns: %TRUE if the range with data after @offset exists.
454  *
455  * Since: 1.4
456  */
457 gboolean
458 gst_sparse_file_get_range_after (GstSparseFile * file, gsize offset,
459     gsize * start, gsize * stop)
460 {
461   GstSparseRange *walk, *result = NULL;
462
463   g_return_val_if_fail (file != NULL, FALSE);
464
465   for (walk = file->ranges; walk; walk = walk->next) {
466     GST_DEBUG ("stop %" G_GSIZE_FORMAT " > %" G_GSIZE_FORMAT,
467         walk->stop, offset);
468     if (walk->stop > offset) {
469       result = walk;
470       break;
471     }
472   }
473   if (result) {
474     if (start)
475       *start = result->start;
476     if (stop)
477       *stop = result->stop;
478   }
479   return result != NULL;
480 }
481
482 /* we don't want to rely on libgio just for g_io_error_from_errno() */
483 static GstSparseFileIOErrorEnum
484 gst_sparse_file_io_error_from_errno (gint err_no)
485 {
486   switch (err_no) {
487 #ifdef EEXIST
488     case EEXIST:
489       return GST_SPARSE_FILE_IO_ERROR_EXISTS;
490       break;
491 #endif
492
493 #ifdef EISDIR
494     case EISDIR:
495       return GST_SPARSE_FILE_IO_ERROR_IS_DIRECTORY;
496       break;
497 #endif
498
499 #ifdef EACCES
500     case EACCES:
501       return GST_SPARSE_FILE_IO_ERROR_PERMISSION_DENIED;
502       break;
503 #endif
504
505 #ifdef ENAMETOOLONG
506     case ENAMETOOLONG:
507       return GST_SPARSE_FILE_IO_ERROR_FILENAME_TOO_LONG;
508       break;
509 #endif
510
511 #ifdef ENOENT
512     case ENOENT:
513       return GST_SPARSE_FILE_IO_ERROR_NOT_FOUND;
514       break;
515 #endif
516
517 #ifdef ENOTDIR
518     case ENOTDIR:
519       return GST_SPARSE_FILE_IO_ERROR_NOT_DIRECTORY;
520       break;
521 #endif
522
523 #ifdef EROFS
524     case EROFS:
525       return GST_SPARSE_FILE_IO_ERROR_READ_ONLY;
526       break;
527 #endif
528
529 #ifdef ELOOP
530     case ELOOP:
531       return GST_SPARSE_FILE_IO_ERROR_TOO_MANY_LINKS;
532       break;
533 #endif
534
535 #ifdef ENOSPC
536     case ENOSPC:
537       return GST_SPARSE_FILE_IO_ERROR_NO_SPACE;
538       break;
539 #endif
540
541 #ifdef ENOMEM
542     case ENOMEM:
543       return GST_SPARSE_FILE_IO_ERROR_NO_SPACE;
544       break;
545 #endif
546
547 #ifdef EINVAL
548     case EINVAL:
549       return GST_SPARSE_FILE_IO_ERROR_INVALID_ARGUMENT;
550       break;
551 #endif
552
553 #ifdef EPERM
554     case EPERM:
555       return GST_SPARSE_FILE_IO_ERROR_PERMISSION_DENIED;
556       break;
557 #endif
558
559 #ifdef ECANCELED
560     case ECANCELED:
561       return GST_SPARSE_FILE_IO_ERROR_CANCELLED;
562       break;
563 #endif
564
565       /* ENOTEMPTY == EEXIST on AIX for backward compatibility reasons */
566 #if defined (ENOTEMPTY) && (!defined (EEXIST) || (ENOTEMPTY != EEXIST))
567     case ENOTEMPTY:
568       return GST_SPARSE_FILE_IO_ERROR_NOT_EMPTY;
569       break;
570 #endif
571
572 #ifdef ENOTSUP
573     case ENOTSUP:
574       return GST_SPARSE_FILE_IO_ERROR_NOT_SUPPORTED;
575       break;
576 #endif
577
578       /* EOPNOTSUPP == ENOTSUP on Linux, but POSIX considers them distinct */
579 #if defined (EOPNOTSUPP) && (!defined (ENOTSUP) || (EOPNOTSUPP != ENOTSUP))
580     case EOPNOTSUPP:
581       return GST_SPARSE_FILE_IO_ERROR_NOT_SUPPORTED;
582       break;
583 #endif
584
585 #ifdef EPROTONOSUPPORT
586     case EPROTONOSUPPORT:
587       return GST_SPARSE_FILE_IO_ERROR_NOT_SUPPORTED;
588       break;
589 #endif
590
591 #ifdef ESOCKTNOSUPPORT
592     case ESOCKTNOSUPPORT:
593       return GST_SPARSE_FILE_IO_ERROR_NOT_SUPPORTED;
594       break;
595 #endif
596
597 #ifdef EPFNOSUPPORT
598     case EPFNOSUPPORT:
599       return GST_SPARSE_FILE_IO_ERROR_NOT_SUPPORTED;
600       break;
601 #endif
602
603 #ifdef EAFNOSUPPORT
604     case EAFNOSUPPORT:
605       return GST_SPARSE_FILE_IO_ERROR_NOT_SUPPORTED;
606       break;
607 #endif
608
609 #ifdef ETIMEDOUT
610     case ETIMEDOUT:
611       return GST_SPARSE_FILE_IO_ERROR_TIMED_OUT;
612       break;
613 #endif
614
615 #ifdef EBUSY
616     case EBUSY:
617       return GST_SPARSE_FILE_IO_ERROR_BUSY;
618       break;
619 #endif
620
621 #ifdef EWOULDBLOCK
622     case EWOULDBLOCK:
623       return GST_SPARSE_FILE_IO_ERROR_WOULD_BLOCK;
624       break;
625 #endif
626
627       /* EWOULDBLOCK == EAGAIN on most systems, but POSIX considers them distinct */
628 #if defined (EAGAIN) && (!defined (EWOULDBLOCK) || (EWOULDBLOCK != EAGAIN))
629     case EAGAIN:
630       return GST_SPARSE_FILE_IO_ERROR_WOULD_BLOCK;
631       break;
632 #endif
633
634 #ifdef EMFILE
635     case EMFILE:
636       return GST_SPARSE_FILE_IO_ERROR_TOO_MANY_OPEN_FILES;
637       break;
638 #endif
639
640 #ifdef EADDRINUSE
641     case EADDRINUSE:
642       return GST_SPARSE_FILE_IO_ERROR_ADDRESS_IN_USE;
643       break;
644 #endif
645
646 #ifdef EHOSTUNREACH
647     case EHOSTUNREACH:
648       return GST_SPARSE_FILE_IO_ERROR_HOST_UNREACHABLE;
649       break;
650 #endif
651
652 #ifdef ENETUNREACH
653     case ENETUNREACH:
654       return GST_SPARSE_FILE_IO_ERROR_NETWORK_UNREACHABLE;
655       break;
656 #endif
657
658 #ifdef ECONNREFUSED
659     case ECONNREFUSED:
660       return GST_SPARSE_FILE_IO_ERROR_CONNECTION_REFUSED;
661       break;
662 #endif
663
664 #ifdef EPIPE
665     case EPIPE:
666       return GST_SPARSE_FILE_IO_ERROR_BROKEN_PIPE;
667       break;
668 #endif
669
670     default:
671       return GST_SPARSE_FILE_IO_ERROR_FAILED;
672       break;
673   }
674 }