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  {
63  LOG(VB_FILE, LOG_DEBUG, "Delete thread self-terminating due to idle.");
64  }
65 
66  RunEpilog();
67 }
68 
69 bool DeleteThread::AddFile(const QString& path)
70 {
71  // check if a file exists, and add to the list of new files to be deleted
72  QFileInfo finfo(path);
73  if (!finfo.exists())
74  return false;
75 
76  QMutexLocker lock(&m_newlock);
77  auto *handler = new DeleteHandler(path);
78  m_newfiles << handler;
79  return true;
80 }
81 
83 {
84  handler->IncrRef();
85  QMutexLocker lock(&m_newlock);
86  m_newfiles << handler;
87  return true;
88 }
89 
91 {
92  // loop through new files, unlinking and adding for deletion
93  // until none are left
94 
95  QDateTime ctime = MythDate::current();
96 
97  while (true)
98  {
99  // pull a new path from the stack
100  DeleteHandler *handler = nullptr;
101  {
102  QMutexLocker lock(&m_newlock);
103  if (m_newfiles.isEmpty())
104  break;
105  handler = m_newfiles.takeFirst();
106  }
107 
108  // empty path given to delete thread, this should not happen
109  //if (path.isEmpty())
110  // continue;
111 
112  QString path = handler->m_path;
113  QByteArray cpath_ba = handler->m_path.toLocal8Bit();
114  const char *cpath = cpath_ba.constData();
115 
116  QFileInfo finfo(handler->m_path);
117  if (finfo.isSymLink())
118  {
119  if (m_link)
120  {
121  // if file is a symlink and symlinks are processed,
122  // grab the target of the link, and attempt to unlink
123  // the link itself
124  QString tmppath = getSymlinkTarget(handler->m_path);
125 
126  if (unlink(cpath))
127  {
128  LOG(VB_GENERAL, LOG_ERR,
129  QString("Error deleting '%1' -> '%2': ")
130  .arg(handler->m_path, tmppath) + ENO);
131  handler->DeleteFailed();
132  handler->DecrRef();
133  continue;
134  }
135 
136  // if successful, emit that the link has been removed,
137  // and continue processing the target of the link as
138  // normal
139  //
140  // this may cause problems in which the link is unlinked
141  // signalling the matching metadata for removal, but the
142  // target itself fails, resulting in a spurious file in
143  // an external directory with no link into mythtv
144  handler->DeleteSucceeded();
145  handler->m_path = tmppath;
146  cpath_ba = handler->m_path.toLocal8Bit();
147  cpath = cpath_ba.constData();
148  }
149  else
150  {
151  // symlinks are not followed, so unlink the link
152  // itself and continue
153  if (unlink(cpath))
154  {
155  LOG(VB_GENERAL, LOG_ERR,
156  QString("Error deleting '%1': count not unlink ")
157  .arg(path) + ENO);
158  handler->DeleteFailed();
159  }
160  else
161  {
162  handler->DeleteFailed();
163  }
164 
165  handler->DecrRef();
166  continue;
167  }
168  }
169 
170  // open the file so it can be unlinked without immediate deletion
171  LOG(VB_FILE, LOG_INFO, QString("About to unlink/delete file: '%1'")
172  .arg(handler->m_path));
173  int fd = open(cpath, O_WRONLY);
174  if (fd == -1)
175  {
176  LOG(VB_FILE, LOG_INFO, QString("About to unlink/delete file"));
177 
178  QDir dir(cpath);
179  if(MythRemoveDirectory(dir))
180  {
181  LOG(VB_GENERAL, LOG_ERR,
182  QString("Error deleting '%1': is no directory ")
183  .arg(cpath) + ENO);
184  handler->DeleteFailed();
185  handler->DecrRef();
186  continue;
187  }
188  }
189  // unlink the file so as soon as it is closed, the system will
190  // delete it from the filesystem
191  else if (unlink(cpath))
192  {
193  LOG(VB_GENERAL, LOG_ERR,
194  QString("Error deleting '%1': could not unlink ")
195  .arg(path) + ENO);
196  handler->DeleteFailed();
197  close(fd);
198  handler->DecrRef();
199  continue;
200  }
201 
202  handler->DeleteSucceeded();
203 
204  // insert the file into a queue of opened references to be deleted
205  handler->m_fd = fd;
206  handler->m_size = finfo.size();
207  handler->m_wait = ctime.addSecs(3); // delay deletion a bit to allow
208  // UI to get any needed IO time
209 
210  m_files << handler;
211  }
212 }
213 
215 {
216  // im the only thread accessing this, so no need for a lock
217  if (m_files.empty())
218  return;
219 
220  QDateTime ctime = MythDate::current();
221 
222  // only operate on one file at a time
223  // delete that file completely before moving onto the next
224  while (true)
225  {
226  DeleteHandler *handler = m_files.first();
227 
228  // first file in the list has been delayed for deletion
229  if (handler->m_wait > ctime)
230  break;
231 
232  if (m_slow)
233  {
234  handler->m_size -= m_increment;
235  int err = ftruncate(handler->m_fd, handler->m_size);
236 
237  if (err)
238  {
239  LOG(VB_GENERAL, LOG_ERR, QString("Error truncating '%1'")
240  .arg(handler->m_path) + ENO);
241  handler->m_size = 0;
242  }
243  }
244  else
245  {
246  handler->m_size = 0;
247  }
248 
249  if (handler->m_size == 0)
250  {
251  handler->Close();
252  m_files.removeFirst();
253  handler->DecrRef();
254  }
255 
256  // fast delete can close out all, but slow delete needs
257  // to return to sleep
258  if (m_slow || m_files.empty())
259  break;
260  }
261 }
bool
bool
Definition: pxsup2dast.c:30
ENO
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:74
ReferenceCounter::DecrRef
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
Definition: referencecounter.cpp:124
mythdb.h
DeleteHandler::m_fd
int m_fd
Definition: fileserverutil.h:40
DeleteThread::AddFile
bool AddFile(const QString &path)
Definition: deletethread.cpp:69
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:214
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:15
close
#define close
Definition: compat.h:43
MythRemoveDirectory
bool MythRemoveDirectory(QDir &aDir)
Definition: mythmiscutil.cpp:759
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:90
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:57
MythCoreContext::GetNumSetting
int GetNumSetting(const QString &key, int defaultval=0)
Definition: mythcorecontext.cpp:918
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:100
DeleteThread::m_files
QList< DeleteHandler * > m_files
Definition: deletethread.h:41
DeleteThread::m_slow
bool m_slow
Definition: deletethread.h:34