MythTV  master
mythiowrapper.cpp
Go to the documentation of this file.
1 // Qt
2 #include <QFile>
3 #include <QMap>
4 #include <QUrl>
5 #include <QReadWriteLock>
6 
7 // MythTV
8 #include "mythconfig.h"
9 #include "compat.h"
10 #include "mythcorecontext.h"
11 #include "mythlogging.h"
12 #include "remotefile.h"
13 #include "io/mythmediabuffer.h"
14 #include "mythiowrapper.h"
15 
16 // Std
17 #if defined(_WIN32)
18 #include <windows.h>
19 #else
20 #include <dlfcn.h>
21 #endif
22 #include <cstdio>
23 #include <dirent.h>
24 #include <fcntl.h>
25 #include <sys/stat.h>
26 #include <sys/types.h>
27 #include <unistd.h>
28 
30 {
31  public:
32  MythIOCallback(void* Object, callback_t Callback)
33  : m_object(Object),
34  m_callback(Callback)
35  {
36  }
37 
38  void *m_object;
40 };
41 
42 static const int s_maxID = 1024 * 1024;
43 
44 static QReadWriteLock s_fileWrapperLock;
45 static QHash<int, MythMediaBuffer*> s_buffers;
46 static QHash<int, RemoteFile*> s_remotefiles;
47 static QHash<int, int> s_localfiles;
48 static QHash<int, QString> s_filenames;
49 
50 static QReadWriteLock s_dirWrapperLock;
51 static QHash<int, QStringList> s_remotedirs;
52 static QHash<int, int> s_remotedirPositions;
53 static QHash<int, QString> s_dirnames;
54 static QHash<int, DIR*> s_localdirs;
55 
56 static QMutex s_callbackLock;
57 static QMultiHash<QString, MythIOCallback> s_fileOpenCallbacks;
58 
59 #define LOC QString("MythIOWrap: ")
60 
61 extern "C" {
62 
63 static int GetNextFileID(void)
64 {
65  int id = 100000;
66 
67  for (; id < s_maxID; ++id)
68  if (!s_localfiles.contains(id) && !s_remotefiles.contains(id) && !s_buffers.contains(id))
69  break;
70 
71  if (id == s_maxID)
72  LOG(VB_GENERAL, LOG_ERR, LOC + "Too many files are open.");
73 
74  LOG(VB_FILE, LOG_DEBUG, LOC + QString("GetNextFileID: '%1'").arg(id));
75  return id;
76 }
77 
78 void MythFileOpenRegisterCallback(const char *Pathname, void* Object, callback_t Func)
79 {
80  QMutexLocker locker(&s_callbackLock);
81  QString path(Pathname);
82  if (s_fileOpenCallbacks.contains(path))
83  {
84  // if we already have a callback registered for this path with this
85  // object then remove the callback and return (i.e. end callback)
86  QMutableHashIterator<QString,MythIOCallback> it(s_fileOpenCallbacks);
87  while (it.hasNext())
88  {
89  it.next();
90  if (Object == it.value().m_object)
91  {
92  it.remove();
93  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Removing fileopen callback for %1").arg(path));
94  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("%1 callbacks remaining").arg(s_fileOpenCallbacks.size()));
95  return;
96  }
97  }
98  }
99 
100  s_fileOpenCallbacks.insert(path, { Object, Func });
101  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Added fileopen callback for %1").arg(path));
102  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("%1 callbacks open").arg(s_fileOpenCallbacks.size()));
103 }
104 
105 int MythFileCheck(int Id)
106 {
107  LOG(VB_FILE, LOG_DEBUG, QString("MythFileCheck: '%1')").arg(Id));
108  QWriteLocker locker(&s_fileWrapperLock);
109  return (s_localfiles.contains(Id) || s_remotefiles.contains(Id) || s_buffers.contains(Id)) ? 1 : 0;
110 }
111 
112 int MythFileOpen(const char *Pathname, int Flags)
113 {
114  LOG(VB_FILE, LOG_DEBUG, QString("MythFileOpen('%1', %2)").arg(Pathname).arg(Flags));
115 
116  struct stat fileinfo {};
117  if (MythFileStat(Pathname, &fileinfo))
118  return -1;
119 
120  // libmythdvdnav tries to open() a dir
121  if (S_ISDIR(fileinfo.st_mode))
122  {
123  errno = EISDIR;
124  return -1;
125  }
126 
127  int fileID = -1;
128  if (strncmp(Pathname, "myth://", 7) != 0)
129  {
130  int lfd = open(Pathname, Flags);
131  if (lfd < 0)
132  return -1;
133 
134  s_fileWrapperLock.lockForWrite();
135  fileID = GetNextFileID();
136  s_localfiles[fileID] = lfd;
137  s_filenames[fileID] = Pathname;
138  s_fileWrapperLock.unlock();
139  }
140  else
141  {
142  MythMediaBuffer *buffer = nullptr;
143  RemoteFile *rf = nullptr;
144 
145  if ((fileinfo.st_size < 512) && (fileinfo.st_mtime < (time(nullptr) - 300)))
146  {
147  if (Flags & O_WRONLY)
148  rf = new RemoteFile(Pathname, true, false); // Writeable
149  else
150  rf = new RemoteFile(Pathname, false, true); // Read-Only
151  if (!rf)
152  return -1;
153  }
154  else
155  {
156  if (Flags & O_WRONLY)
157  {
158  buffer = MythMediaBuffer::Create(Pathname, true, false,
159  MythMediaBuffer::kDefaultOpenTimeout, true); // Writeable
160  }
161  else
162  {
163  buffer = MythMediaBuffer::Create(Pathname, false, true,
164  MythMediaBuffer::kDefaultOpenTimeout, true); // Read-Only
165  }
166 
167  if (!buffer)
168  return -1;
169 
170  buffer->Start();
171  }
172 
173  s_fileWrapperLock.lockForWrite();
174  fileID = GetNextFileID();
175 
176  if (rf)
177  s_remotefiles[fileID] = rf;
178  else if (buffer)
179  s_buffers[fileID] = buffer;
180 
181  s_filenames[fileID] = Pathname;
182  s_fileWrapperLock.unlock();
183  }
184 
185  s_callbackLock.lock();
186  if (!s_fileOpenCallbacks.isEmpty())
187  {
188  QString path(Pathname);
189  QHashIterator<QString,MythIOCallback> it(s_fileOpenCallbacks);
190  while (it.hasNext())
191  {
192  it.next();
193  if (path.startsWith(it.key()))
194  it.value().m_callback(it.value().m_object);
195  }
196  }
197  s_callbackLock.unlock();
198 
199  return fileID;
200 }
201 
202 int MythfileClose(int FileID)
203 {
204  LOG(VB_FILE, LOG_DEBUG, LOC + QString("MythfileClose: '%1").arg(FileID));
205 
206  // FIXME - surely this needs to hold write lock?
207  QReadLocker locker(&s_fileWrapperLock);
208  if (s_buffers.contains(FileID))
209  {
210  MythMediaBuffer *buffer = s_buffers[FileID];
211  s_buffers.remove(FileID);
212  delete buffer;
213  return 0;
214  }
215 
216  if (s_remotefiles.contains(FileID))
217  {
218  RemoteFile *rf = s_remotefiles[FileID];
219  s_remotefiles.remove(FileID);
220  delete rf;
221  return 0;
222  }
223 
224  if (s_localfiles.contains(FileID))
225  {
226  close(s_localfiles[FileID]);
227  s_localfiles.remove(FileID);
228  return 0;
229  }
230 
231  return -1;
232 }
233 
234 #ifdef _WIN32
235 # undef lseek
236 # define lseek _lseeki64
237 # undef off_t
238 # define off_t off64_t
239 #endif
240 off_t MythFileSeek(int FileID, off_t Offset, int Whence)
241 {
242  off_t result = -1;
243 
244  LOG(VB_FILE, LOG_DEBUG, LOC + QString("MythFileSeek(%1, %2, %3)")
245  .arg(FileID).arg(Offset).arg(Whence));
246 
247  s_fileWrapperLock.lockForRead();
248  if (s_buffers.contains(FileID))
249  result = s_buffers[FileID]->Seek(Offset, Whence);
250  else if (s_remotefiles.contains(FileID))
251  result = s_remotefiles[FileID]->Seek(Offset, Whence);
252  else if (s_localfiles.contains(FileID))
253  result = lseek(s_localfiles[FileID], Offset, Whence);
254  s_fileWrapperLock.unlock();
255 
256  return result;
257 }
258 
259 off_t MythFileTell(int FileID)
260 {
261  off_t result = -1;
262 
263  LOG(VB_FILE, LOG_DEBUG, LOC + QString("MythFileTell(%1)").arg(FileID));
264 
265  s_fileWrapperLock.lockForRead();
266  if (s_buffers.contains(FileID))
267  result = s_buffers[FileID]->Seek(0, SEEK_CUR);
268  else if (s_remotefiles.contains(FileID))
269  result = s_remotefiles[FileID]->Seek(0, SEEK_CUR);
270  else if (s_localfiles.contains(FileID))
271  result = lseek(s_localfiles[FileID], 0, SEEK_CUR);
272  s_fileWrapperLock.unlock();
273 
274  return result;
275 }
276 
277 #ifdef _WIN32
278 # undef lseek
279 # undef off_t
280 #endif
281 
282 ssize_t MythFileRead(int FileID, void *Buffer, size_t Count)
283 {
284  ssize_t result = -1;
285 
286  LOG(VB_FILE, LOG_DEBUG, LOC + QString("MythFileRead(%1, %2, %3)")
287  .arg(FileID).arg(reinterpret_cast<long long>(Buffer)).arg(Count));
288 
289  s_fileWrapperLock.lockForRead();
290  if (s_buffers.contains(FileID))
291  result = s_buffers[FileID]->Read(Buffer, static_cast<int>(Count));
292  else if (s_remotefiles.contains(FileID))
293  result = s_remotefiles[FileID]->Read(Buffer, static_cast<int>(Count));
294  else if (s_localfiles.contains(FileID))
295  result = read(s_localfiles[FileID], Buffer, Count);
296  s_fileWrapperLock.unlock();
297 
298  return result;
299 }
300 
301 ssize_t MythFileWrite(int FileID, void *Buffer, size_t Count)
302 {
303  ssize_t result = -1;
304 
305  LOG(VB_FILE, LOG_DEBUG, LOC + QString("MythFileWrite(%1, %2, %3)")
306  .arg(FileID).arg(reinterpret_cast<long long>(Buffer)).arg(Count));
307 
308  s_fileWrapperLock.lockForRead();
309  if (s_buffers.contains(FileID))
310  result = s_buffers[FileID]->Write(Buffer, static_cast<uint>(Count));
311  else if (s_remotefiles.contains(FileID))
312  result = s_remotefiles[FileID]->Write(Buffer, static_cast<int>(Count));
313  else if (s_localfiles.contains(FileID))
314  result = write(s_localfiles[FileID], Buffer, Count);
315  s_fileWrapperLock.unlock();
316 
317  return result;
318 }
319 
320 int MythFileStat(const char *Path, struct stat *Buf)
321 {
322  LOG(VB_FILE, LOG_DEBUG, LOC + QString("MythfileStat('%1', %2)")
323  .arg(Path).arg(reinterpret_cast<long long>(Buf)));
324 
325  if (strncmp(Path, "myth://", 7) == 0)
326  {
327  bool res = RemoteFile::Exists(Path, Buf);
328  if (res)
329  return 0;
330  }
331 
332  return stat(Path, Buf);
333 }
334 
335 int MythFileStatFD(int FileID, struct stat *Buf)
336 {
337  LOG(VB_FILE, LOG_DEBUG, LOC + QString("MythFileStatFD(%1, %2)")
338  .arg(FileID).arg(reinterpret_cast<long long>(Buf)));
339 
340  s_fileWrapperLock.lockForRead();
341  if (!s_filenames.contains(FileID))
342  {
343  s_fileWrapperLock.unlock();
344  return -1;
345  }
346  QString filename = s_filenames[FileID];
347  s_fileWrapperLock.unlock();
348 
349  return MythFileStat(filename.toLocal8Bit().constData(), Buf);
350 }
351 
352 /*
353  * This function exists for the use of dvd_reader.c, thus the return
354  * value of int instead of bool. C doesn't have a bool type.
355  */
356 int MythFileExists(const char *Path, const char *File)
357 {
358  LOG(VB_FILE, LOG_DEBUG, LOC + QString("MythFileExists('%1', '%2')")
359  .arg(Path).arg(File));
360 
361  bool ret = false;
362  if (strncmp(Path, "myth://", 7) == 0)
363  ret = RemoteFile::Exists(QString("%1/%2").arg(Path).arg(File));
364  else
365  ret = QFile::exists(QString("%1/%2").arg(Path).arg(File));
366  return static_cast<int>(ret);
367 }
368 
369 static int GetNextDirID(void)
370 {
371  int id = 100000;
372  for (; id < s_maxID; ++id)
373  if (!s_localdirs.contains(id) && !s_remotedirs.contains(id))
374  break;
375 
376  if (id == s_maxID)
377  LOG(VB_GENERAL, LOG_ERR, LOC + "Too many directories are open.");
378  LOG(VB_FILE, LOG_DEBUG, LOC + QString("GetNextDirID: '%1'").arg(id));
379 
380  return id;
381 }
382 
383 int MythDirCheck(int DirID)
384 {
385  LOG(VB_FILE, LOG_DEBUG, QString("MythDirCheck: '%1'").arg(DirID));
386  s_dirWrapperLock.lockForWrite();
387  int result = ((s_localdirs.contains(DirID) || s_remotedirs.contains(DirID))) ? 1 : 0;
388  s_dirWrapperLock.unlock();
389  return result;
390 }
391 
392 int MythDirOpen(const char *DirName)
393 {
394  LOG(VB_FILE, LOG_DEBUG, LOC + QString("MythDirOpen: '%1'").arg(DirName));
395 
396  if (strncmp(DirName, "myth://", 7) != 0)
397  {
398  DIR *dir = opendir(DirName);
399  if (dir)
400  {
401  s_dirWrapperLock.lockForWrite();
402  int id = GetNextDirID();
403  s_localdirs[id] = dir;
404  s_dirnames[id] = DirName;
405  s_dirWrapperLock.unlock();
406  return id;
407  }
408  }
409  else
410  {
411  QStringList list;
412  QUrl qurl(DirName);
413  QString storageGroup = qurl.userName();
414 
415  list.clear();
416  if (storageGroup.isEmpty())
417  storageGroup = "Default";
418 
419  list << "QUERY_SG_GETFILELIST" << qurl.host() << storageGroup;
420 
421  QString path = qurl.path();
422  if (!qurl.fragment().isEmpty())
423  path += "#" + qurl.fragment();
424 
425  list << path << "1";
426 
427  bool ok = gCoreContext->SendReceiveStringList(list);
428 
429  if ((!ok) || ((list.size() == 1) && (list[0] == "EMPTY LIST")))
430  list.clear();
431 
432  s_dirWrapperLock.lockForWrite();
433  int id = GetNextDirID();
434  s_remotedirs[id] = list;
435  s_remotedirPositions[id] = 0;
436  s_dirnames[id] = DirName;
437  s_dirWrapperLock.unlock();
438  return id;
439  }
440 
441  return 0;
442 }
443 
444 int MythDirClose(int DirID)
445 {
446  LOG(VB_FILE, LOG_DEBUG, LOC + QString("MythDirClose: '%1'").arg(DirID));
447 
448  QReadLocker locker(&s_dirWrapperLock);
449  if (s_remotedirs.contains(DirID))
450  {
451  s_remotedirs.remove(DirID);
452  s_remotedirPositions.remove(DirID);
453  return 0;
454  }
455 
456  if (s_localdirs.contains(DirID))
457  {
458  int result = closedir(s_localdirs[DirID]);
459  if (result == 0)
460  s_localdirs.remove(DirID);
461  return result;
462  }
463 
464  return -1;
465 }
466 
467 char *MythDirRead(int DirID)
468 {
469  LOG(VB_FILE, LOG_DEBUG, LOC + QString("MythDirRead: '%1'").arg(DirID));
470 
471  QReadLocker locker(&s_dirWrapperLock);
472  if (s_remotedirs.contains(DirID))
473  {
474  int pos = s_remotedirPositions[DirID];
475  if (s_remotedirs[DirID].size() >= (pos + 1))
476  {
477  char* result = strdup(s_remotedirs[DirID][pos].toLocal8Bit().constData());
478  pos++;
479  s_remotedirPositions[DirID] = pos;
480  return result;
481  }
482  }
483  else if (s_localdirs.contains(DirID))
484  {
485  struct dirent *dir = nullptr;
486  // glibc deprecated readdir_r in version 2.24,
487  // cppcheck-suppress readdirCalled
488  if ((dir = readdir(s_localdirs[DirID])) != nullptr)
489  return strdup(dir->d_name);
490  }
491 
492  return nullptr;
493 }
494 
495 } // extern "C"
496 
MythFileStatFD
int MythFileStatFD(int FileID, struct stat *Buf)
Definition: mythiowrapper.cpp:335
s_localdirs
static QHash< int, DIR * > s_localdirs
Definition: mythiowrapper.cpp:54
MythDirClose
int MythDirClose(int DirID)
Definition: mythiowrapper.cpp:444
MythCoreContext::SendReceiveStringList
bool SendReceiveStringList(QStringList &strlist, bool quickTimeout=false, bool block=true)
Send a message to the backend and wait for a response.
Definition: mythcorecontext.cpp:1377
callback_t
void(* callback_t)(void *)
Definition: mythiowrapper.h:20
RemoteFile::Exists
static bool Exists(const QString &url, struct stat *fileinfo)
Definition: remotefile.cpp:462
discid.disc.read
def read(device=None, features=[])
Definition: disc.py:35
MythMediaBuffer::Create
static MythMediaBuffer * Create(const QString &Filename, bool Write, bool UseReadAhead=true, int Timeout=kDefaultOpenTimeout, bool StreamOnly=false)
Creates a RingBuffer instance.
Definition: mythmediabuffer.cpp:100
MythIOCallback
Definition: mythiowrapper.cpp:30
MythDirRead
char * MythDirRead(int DirID)
Definition: mythiowrapper.cpp:467
s_dirnames
static QHash< int, QString > s_dirnames
Definition: mythiowrapper.cpp:53
mythburn.write
def write(text, progress=True)
Definition: mythburn.py:308
arg
arg(title).arg(filename).arg(doDelete))
MythfileClose
int MythfileClose(int FileID)
Definition: mythiowrapper.cpp:202
RemoteFile
Definition: remotefile.h:18
s_maxID
static const int s_maxID
Definition: mythiowrapper.cpp:42
MythMediaBuffer
Definition: mythmediabuffer.h:50
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23
MythMediaBuffer::kDefaultOpenTimeout
static const int kDefaultOpenTimeout
Definition: mythmediabuffer.h:61
MythFileCheck
int MythFileCheck(int Id)
Definition: mythiowrapper.cpp:105
MythIOCallback::m_object
void * m_object
Definition: mythiowrapper.cpp:38
MythFileOpenRegisterCallback
void MythFileOpenRegisterCallback(const char *Pathname, void *Object, callback_t Func)
Definition: mythiowrapper.cpp:78
MythFileRead
ssize_t MythFileRead(int FileID, void *Buffer, size_t Count)
Definition: mythiowrapper.cpp:282
s_fileOpenCallbacks
static QMultiHash< QString, MythIOCallback > s_fileOpenCallbacks
Definition: mythiowrapper.cpp:57
close
#define close
Definition: compat.h:16
s_remotedirs
static QHash< int, QStringList > s_remotedirs
Definition: mythiowrapper.cpp:51
lseek
#define lseek
Definition: mythiowrapper.cpp:236
mythlogging.h
s_buffers
static QHash< int, MythMediaBuffer * > s_buffers
Definition: mythiowrapper.cpp:45
remotefile.h
s_localfiles
static QHash< int, int > s_localfiles
Definition: mythiowrapper.cpp:47
LOC
#define LOC
Definition: mythiowrapper.cpp:59
compat.h
filename
QString filename
Definition: mythplugins/mytharchive/mytharchivehelper/main.cpp:640
MythFileTell
off_t MythFileTell(int FileID)
Definition: mythiowrapper.cpp:259
s_dirWrapperLock
static QReadWriteLock s_dirWrapperLock
Definition: mythiowrapper.cpp:50
MythFileStat
int MythFileStat(const char *Path, struct stat *Buf)
Definition: mythiowrapper.cpp:320
s_remotedirPositions
static QHash< int, int > s_remotedirPositions
Definition: mythiowrapper.cpp:52
GetNextFileID
static int GetNextFileID(void)
Definition: mythiowrapper.cpp:63
uint
unsigned int uint
Definition: compat.h:140
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:57
s_filenames
static QHash< int, QString > s_filenames
Definition: mythiowrapper.cpp:48
GetNextDirID
static int GetNextDirID(void)
Definition: mythiowrapper.cpp:369
mythmediabuffer.h
off_t
#define off_t
Definition: mythiowrapper.cpp:238
Buffer
Definition: MythExternControl.h:37
mythcorecontext.h
s_callbackLock
static QMutex s_callbackLock
Definition: mythiowrapper.cpp:56
dir
QDir dir
Definition: mythplugins/mytharchive/mytharchivehelper/main.cpp:1177
MythMediaBuffer::Start
void Start(void)
Starts the read-ahead thread.
Definition: mythmediabuffer.cpp:613
MythFileOpen
int MythFileOpen(const char *Pathname, int Flags)
Definition: mythiowrapper.cpp:112
MythIOCallback::MythIOCallback
MythIOCallback(void *Object, callback_t Callback)
Definition: mythiowrapper.cpp:32
MythDirOpen
int MythDirOpen(const char *DirName)
Definition: mythiowrapper.cpp:392
Buf
IFSPoint * Buf
Definition: ifs.cpp:148
s_remotefiles
static QHash< int, RemoteFile * > s_remotefiles
Definition: mythiowrapper.cpp:46
MythIOCallback::m_callback
callback_t m_callback
Definition: mythiowrapper.cpp:39
s_fileWrapperLock
static QReadWriteLock s_fileWrapperLock
Definition: mythiowrapper.cpp:44
MythDirCheck
int MythDirCheck(int DirID)
Definition: mythiowrapper.cpp:383
MythFileWrite
ssize_t MythFileWrite(int FileID, void *Buffer, size_t Count)
Definition: mythiowrapper.cpp:301
MythFileSeek
off_t MythFileSeek(int FileID, off_t Offset, int Whence)
Definition: mythiowrapper.cpp:240
mythiowrapper.h
MythFileExists
int MythFileExists(const char *Path, const char *File)
Definition: mythiowrapper.cpp:356