MythTV  master
deletethread.cpp
Go to the documentation of this file.
1 #include <cstdlib>
2 #include <iostream>
3 #include <fcntl.h>
4 
5 #include <QList>
6 #include <QTimer>
7 #include <QString>
8 #include <QFileInfo>
9 #include <QDateTime>
10 #include <QStringList>
11 #include <QMutexLocker>
12 
14 #include "libmythbase/mythdb.h"
17 
19 
20 /*
21  Rather than attempt to calculate a delete speed from tuner card information
22  that may be completely irrelevent to a machine that does not record, just
23  choose a reasonable value.
24 
25  38 Mbps (full QAM-256 multiplex) * 4 tuners = 9961472B/0.5s
26 */
27 
29  MThread("Delete")
30 {
31  m_slow = (bool) gCoreContext->GetNumSetting("TruncateDeletesSlowly", 0);
32  m_link = (bool) gCoreContext->GetNumSetting("DeletesFollowLinks", 0);
33 }
34 
36 {
37  RunProlog();
38 
39  LOG(VB_FILE, LOG_DEBUG, "Spawning new delete thread.");
40 
41  while (gCoreContext && m_run)
42  {
43  // loop through any stored files every half second
44  ProcessNew();
45  ProcessOld();
46  usleep(0.5s);
47  }
48 
49  if (!m_files.empty())
50  {
51  // this will only happen if the program is closing, so fast
52  // deletion is not a problem
53  QList<DeleteHandler*>::iterator i;
54  for (i = m_files.begin(); i != m_files.end(); ++i)
55  {
56  (*i)->Close();
57  (*i)->DecrRef();
58  }
59  m_files.clear();
60  }
61  else
62  LOG(VB_FILE, LOG_DEBUG, "Delete thread self-terminating due to idle.");
63 
64  RunEpilog();
65 }
66 
67 bool DeleteThread::AddFile(const QString& path)
68 {
69  // check if a file exists, and add to the list of new files to be deleted
70  QFileInfo finfo(path);
71  if (!finfo.exists())
72  return false;
73 
74  QMutexLocker lock(&m_newlock);
75  auto *handler = new DeleteHandler(path);
76  m_newfiles << handler;
77  return true;
78 }
79 
81 {
82  handler->IncrRef();
83  QMutexLocker lock(&m_newlock);
84  m_newfiles << handler;
85  return true;
86 }
87 
89 {
90  // loop through new files, unlinking and adding for deletion
91  // until none are left
92 
93  QDateTime ctime = MythDate::current();
94 
95  while (true)
96  {
97  // pull a new path from the stack
98  DeleteHandler *handler = nullptr;
99  {
100  QMutexLocker lock(&m_newlock);
101  if (m_newfiles.isEmpty())
102  break;
103  handler = m_newfiles.takeFirst();
104  }
105 
106  // empty path given to delete thread, this should not happen
107  //if (path.isEmpty())
108  // continue;
109 
110  QString path = handler->m_path;
111  QByteArray cpath_ba = handler->m_path.toLocal8Bit();
112  const char *cpath = cpath_ba.constData();
113 
114  QFileInfo finfo(handler->m_path);
115  if (finfo.isSymLink())
116  {
117  if (m_link)
118  {
119  // if file is a symlink and symlinks are processed,
120  // grab the target of the link, and attempt to unlink
121  // the link itself
122  QString tmppath = getSymlinkTarget(handler->m_path);
123 
124  if (unlink(cpath))
125  {
126  LOG(VB_GENERAL, LOG_ERR,
127  QString("Error deleting '%1' -> '%2': ")
128  .arg(handler->m_path, tmppath) + ENO);
129  handler->DeleteFailed();
130  handler->DecrRef();
131  continue;
132  }
133 
134  // if successful, emit that the link has been removed,
135  // and continue processing the target of the link as
136  // normal
137  //
138  // this may cause problems in which the link is unlinked
139  // signalling the matching metadata for removal, but the
140  // target itself fails, resulting in a spurious file in
141  // an external directory with no link into mythtv
142  handler->DeleteSucceeded();
143  handler->m_path = tmppath;
144  cpath_ba = handler->m_path.toLocal8Bit();
145  cpath = cpath_ba.constData();
146  }
147  else
148  {
149  // symlinks are not followed, so unlink the link
150  // itself and continue
151  if (unlink(cpath))
152  {
153  LOG(VB_GENERAL, LOG_ERR,
154  QString("Error deleting '%1': count not unlink ")
155  .arg(path) + ENO);
156  handler->DeleteFailed();
157  }
158  else
159  handler->DeleteFailed();
160 
161  handler->DecrRef();
162  continue;
163  }
164  }
165 
166  // open the file so it can be unlinked without immediate deletion
167  LOG(VB_FILE, LOG_INFO, QString("About to unlink/delete file: '%1'")
168  .arg(handler->m_path));
169  int fd = open(cpath, O_WRONLY);
170  if (fd == -1)
171  {
172  LOG(VB_FILE, LOG_INFO, QString("About to unlink/delete file"));
173 
174  QDir dir(cpath);
175  if(MythRemoveDirectory(dir))
176  {
177  LOG(VB_GENERAL, LOG_ERR,
178  QString("Error deleting '%1': is no directory ")
179  .arg(cpath) + ENO);
180  handler->DeleteFailed();
181  handler->DecrRef();
182  continue;
183  }
184  }
185  // unlink the file so as soon as it is closed, the system will
186  // delete it from the filesystem
187  else if (unlink(cpath))
188  {
189  LOG(VB_GENERAL, LOG_ERR,
190  QString("Error deleting '%1': could not unlink ")
191  .arg(path) + ENO);
192  handler->DeleteFailed();
193  close(fd);
194  handler->DecrRef();
195  continue;
196  }
197 
198  handler->DeleteSucceeded();
199 
200  // insert the file into a queue of opened references to be deleted
201  handler->m_fd = fd;
202  handler->m_size = finfo.size();
203  handler->m_wait = ctime.addSecs(3); // delay deletion a bit to allow
204  // UI to get any needed IO time
205 
206  m_files << handler;
207  }
208 }
209 
211 {
212  // im the only thread accessing this, so no need for a lock
213  if (m_files.empty())
214  return;
215 
216  QDateTime ctime = MythDate::current();
217 
218  // only operate on one file at a time
219  // delete that file completely before moving onto the next
220  while (true)
221  {
222  DeleteHandler *handler = m_files.first();
223 
224  // first file in the list has been delayed for deletion
225  if (handler->m_wait > ctime)
226  break;
227 
228  if (m_slow)
229  {
230  handler->m_size -= m_increment;
231  int err = ftruncate(handler->m_fd, handler->m_size);
232 
233  if (err)
234  {
235  LOG(VB_GENERAL, LOG_ERR, QString("Error truncating '%1'")
236  .arg(handler->m_path) + ENO);
237  handler->m_size = 0;
238  }
239  }
240  else
241  handler->m_size = 0;
242 
243  if (handler->m_size == 0)
244  {
245  handler->Close();
246  m_files.removeFirst();
247  handler->DecrRef();
248  }
249 
250  // fast delete can close out all, but slow delete needs
251  // to return to sleep
252  if (m_slow || m_files.empty())
253  break;
254  }
255 }
bool
bool
Definition: pxsup2dast.c:30
ENO
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:73
ReferenceCounter::DecrRef
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
Definition: referencecounter.cpp:125
mythdb.h
DeleteHandler::m_fd
int m_fd
Definition: fileserverutil.h:40
DeleteThread::AddFile
bool AddFile(const QString &path)
Definition: deletethread.cpp:67
MThread::usleep
static void usleep(std::chrono::microseconds time)
Definition: mthread.cpp:335
getSymlinkTarget
QString getSymlinkTarget(const QString &start_file, QStringList *intermediaries, unsigned maxLinks)
Definition: mythmiscutil.cpp:451
DeleteThread::ProcessOld
void ProcessOld(void)
Definition: deletethread.cpp:210
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MThread::RunProlog
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:196
MythDate::current
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:14
close
#define close
Definition: compat.h:43
MythRemoveDirectory
bool MythRemoveDirectory(QDir &aDir)
Definition: mythmiscutil.cpp:756
DeleteHandler::DeleteSucceeded
virtual void DeleteSucceeded(void)
Definition: fileserverutil.h:33
deletethread.h
mythlogging.h
DeleteThread::m_newlock
QMutex m_newlock
Definition: deletethread.h:39
DeleteHandler
Definition: fileserverutil.h:17
DeleteThread::m_newfiles
QList< DeleteHandler * > m_newfiles
Definition: deletethread.h:38
DeleteThread::ProcessNew
void ProcessNew(void)
Definition: deletethread.cpp:88
DeleteHandler::m_wait
QDateTime m_wait
Definition: fileserverutil.h:42
DeleteHandler::m_size
off_t m_size
Definition: fileserverutil.h:41
MThread::RunEpilog
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:209
DeleteThread::run
void run() override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
Definition: deletethread.cpp:35
DeleteHandler::m_path
QString m_path
Definition: fileserverutil.h:39
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:55
MythCoreContext::GetNumSetting
int GetNumSetting(const QString &key, int defaultval=0)
Definition: mythcorecontext.cpp:911
DeleteThread::m_increment
size_t m_increment
Definition: deletethread.h:33
mythmiscutil.h
DeleteThread::m_link
bool m_link
Definition: deletethread.h:35
mythcorecontext.h
DeleteThread::DeleteThread
DeleteThread(void)
Definition: deletethread.cpp:28
DeleteHandler::DeleteFailed
virtual void DeleteFailed(void)
Definition: fileserverutil.h:34
MThread
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:48
DeleteThread::m_run
bool m_run
Definition: deletethread.h:36
DeleteHandler::Close
void Close(void)
Definition: fileserverutil.cpp:36
ReferenceCounter::IncrRef
virtual int IncrRef(void)
Increments reference count.
Definition: referencecounter.cpp:101
DeleteThread::m_files
QList< DeleteHandler * > m_files
Definition: deletethread.h:41
DeleteThread::m_slow
bool m_slow
Definition: deletethread.h:34