1  /*
2    mkvmerge -- utility for splicing together matroska files
3    from component media subtypes
4 
5    Distributed under the GPL v2
6    see the file COPYING for details
7    or visit https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
8 
9    IO callback class implementation
10 
11    Written by Moritz Bunkus <moritz@bunkus.org>.
12 */
13 
14 #include "common/common_pch.h"
15 
16 #include <stdio.h>
17 #include <stdlib.h>
18 #ifdef HAVE_UNISTD_H
19 #include <unistd.h>
20 #endif
21 #include <sys/stat.h>
22 #include <sys/types.h>
23 
24 #include "common/mm_io_x.h"
25 #include "common/mm_file_io.h"
26 #include "common/mm_file_io_p.h"
27 #if defined(SYS_APPLE)
28 # include "common/fs_sys_helpers.h"
29 #endif
30 
mm_file_io_private_c(std::string const & p_file_name,open_mode const p_mode)31 mm_file_io_private_c::mm_file_io_private_c(std::string const &p_file_name,
32                                            open_mode const p_mode)
33   : file_name{p_file_name}
34   , mode{p_mode}
35 {
36 #if defined(SYS_APPLE)
37   file_name = mtx::sys::normalize_unicode_string(file_name, mtx::sys::unicode_normalization_form_e::d);
38 #endif  // SYS_APPLE
39 
40   const char *cmode;
41 
42   switch (mode) {
43     case MODE_READ:
44       cmode = "rb";
45       break;
46     case MODE_WRITE:
47       cmode = "r+b";
48       break;
49     case MODE_CREATE:
50       cmode = "w+b";
51       break;
52     case MODE_SAFE:
53       cmode = "rb";
54       break;
55     default:
56       throw mtx::invalid_parameter_x();
57   }
58 
59   if ((MODE_WRITE == mode) || (MODE_CREATE == mode))
60     mm_file_io_c::prepare_path(file_name);
61   std::string local_path = g_cc_local_utf8->native(file_name);
62 
63   struct stat st;
64   if ((0 == stat(local_path.c_str(), &st)) && S_ISDIR(st.st_mode))
65     throw mtx::mm_io::open_x{mtx::mm_io::make_error_code()};
66 
67   file = fopen(local_path.c_str(), cmode);
68 
69 #if defined(SYS_APPLE)
70   if (!file && (MODE_CREATE != mode)) {
71     // When reading files on macOS retry with names in NFC as they
72     // might come from other sources, e.g. via NFS mounts from NFC
73     // systems such as Linux.
74     local_path = g_cc_local_utf8->native(mtx::sys::normalize_unicode_string(file_name, mtx::sys::unicode_normalization_form_e::c));
75     file = fopen(local_path.c_str(), cmode);
76   }
77 #endif  // SYS_APPLE
78 
79   if (!file)
80     throw mtx::mm_io::open_x{mtx::mm_io::make_error_code()};
81 }
82 
83 void
setFilePointer(int64_t offset,libebml::seek_mode mode)84 mm_file_io_c::setFilePointer(int64_t offset,
85                              libebml::seek_mode mode) {
86   auto p     = p_func();
87   int whence = mode == libebml::seek_beginning ? SEEK_SET
88              : mode == libebml::seek_end       ? SEEK_END
89              :                                   SEEK_CUR;
90 
91   if (fseeko(p->file, offset, whence) != 0)
92     throw mtx::mm_io::seek_x{mtx::mm_io::make_error_code()};
93 
94   p->current_position = ftello(p->file);
95 }
96 
97 size_t
_write(const void * buffer,size_t size)98 mm_file_io_c::_write(const void *buffer,
99                      size_t size) {
100   auto p          = p_func();
101   size_t bwritten = fwrite(buffer, 1, size, p->file);
102   if (ferror(p->file) != 0)
103     throw mtx::mm_io::read_write_x{mtx::mm_io::make_error_code()};
104 
105   p->current_position += bwritten;
106   p->cached_size       = -1;
107 
108   return bwritten;
109 }
110 
111 uint32_t
_read(void * buffer,size_t size)112 mm_file_io_c::_read(void *buffer,
113                     size_t size) {
114   auto p        = p_func();
115   int64_t bread = fread(buffer, 1, size, p->file);
116 
117   p->current_position += bread;
118 
119   return bread;
120 }
121 
122 void
close()123 mm_file_io_c::close() {
124   auto p = p_func();
125 
126   if (p->file) {
127     if (mm_file_io_private_c::ms_flush_on_close && (p->mode != MODE_READ))
128       fflush(p->file);
129 
130     fclose(p->file);
131     p->file = nullptr;
132   }
133 
134   p->file_name.clear();
135 }
136 
137 bool
eof()138 mm_file_io_c::eof() {
139   return feof(p_func()->file) != 0;
140 }
141 
142 void
clear_eof()143 mm_file_io_c::clear_eof() {
144   clearerr(p_func()->file);
145 }
146 
147 int
truncate(int64_t pos)148 mm_file_io_c::truncate(int64_t pos) {
149   auto p         = p_func();
150   p->cached_size = -1;
151   return ftruncate(fileno(p->file), pos);
152 }
153