vdr  1.7.31
filetransfer.c
Go to the documentation of this file.
1 /*
2  * filetransfer.c: The video file transfer facilities
3  *
4  * See the main source file 'vdr.c' for copyright information and
5  * how to reach the author.
6  *
7  * $Id: $
8  */
9 
10 #include "videodir.h"
11 #include "filetransfer.h"
12 
13 static cString StripLastDirectory(const char *DirName)
14 {
15  if (DirName && *DirName) {
16  cString s(DirName);
17  int l = strlen(*s);
18  const char *p = *s + l;
19  while (l > 0) {
20  if (*p-- == '/')
21  break;
22  l--;
23  }
24  if (l)
25  s = s.Truncate(l);
26  return s;
27  }
28  return NULL;
29 }
30 
31 // --- cCopyingThread --------------------------------------------------------
32 
33 class cCopyingThread : public cThread {
34 private:
35  const char *error;
39 protected:
40  virtual void Action(void);
41 public:
42  cCopyingThread(const char *SourceName, const char *ToFileName, bool DeleteSource = false);
43  virtual ~cCopyingThread();
44  const char *Error(void) { return error; }
45  };
46 
47 cCopyingThread::cCopyingThread(const char *SourceName, const char *TargetName, bool DeleteSource)
48 :cThread("copying"),
49  error(NULL),
50  deleteSource(DeleteSource),
51  source(SourceName),
52  target(TargetName)
53 {
54  // add missing directory delimiters
55  const char *delim = "/";
56  if (!endswith(*source, delim))
57  source = cString::sprintf("%s%s", *source, delim);
58  if (!endswith(*target, delim))
59  target = cString::sprintf("%s%s", *target, delim);
60 
61  Start();
62 }
63 
65 {
66  Cancel(3);
67 }
68 
70 {
71  SetPriority(19);
72  SetIOPriority(7);
73 
74  if (strcmp(*source, *target)) {
75  // validate target directory
76  if (strstr(*target, *source)) {
77  error = "invalid target";
78  return;
79  }
80 
81  // recordings methods require the last directory delimiter to be stripped off
82  cString recname = target;
83  recname.Truncate(strlen(*recname) - 1);
84  Recordings.AddByName(*recname, false);
85 
87  if (!MakeDirs(*target, true)) {
88  error = "MakeDirs";
89  return;
90  }
91 
93  if (rename(*source, *target) == -1) {
94  error = "rename";
95  return;
96  }
97  // delete all empty source directories
98  recname = source;
99  recname.Truncate(strlen(*recname) - 1);
100  recname = StripLastDirectory(*recname);
101  do {
102  if (!RemoveEmptyDirectories(*recname, true))
103  break;
104  recname = StripLastDirectory(*recname);
105  }
106  while (strcmp(*recname, VideoDirectory));
107  }
108  else {
109  int required = DirSizeMB(*source);
110  int available = FreeDiskSpaceMB(*target);
111 
112  // validate free space
113  if (required < available) {
114  cReadDir d(*source);
115  struct dirent *e;
116  bool success = true;
117 
118  // allocate copying buffer
119  const int len = 1024 * 1024;
120  char *buffer = MALLOC(char, len);
121  if (!buffer) {
122  error = "MALLOC";
123  return;
124  }
125 
126  // loop through all files, but skip all sub-directories
127  while (Running() && (e = d.Next()) != NULL) {
128  // skip generic entries
129  if (strcmp(e->d_name, ".") && strcmp(e->d_name, "..") && strcmp(e->d_name, "lost+found")) {
130  cString sourceFile = cString::sprintf("%s%s", *source, e->d_name);
131  cString targetFile = cString::sprintf("%s%s", *target, e->d_name);
132 
133  // copy only regular files
134  struct stat sts;
135  if (!stat(*sourceFile, &sts) && S_ISREG(sts.st_mode)) {
136  int r = -1, w = -1;
137  cUnbufferedFile *inputFile = cUnbufferedFile::Create(*sourceFile, O_RDONLY | O_LARGEFILE);
138  cUnbufferedFile *outputFile = cUnbufferedFile::Create(*targetFile, O_RDWR | O_CREAT | O_LARGEFILE);
139 
140  // validate files
141  if (!inputFile || !outputFile) {
142  success = false;
143  break;
144  }
145 
146  // do actual copy
147  do {
148  r = inputFile->Read(buffer, len);
149  if (r > 0)
150  w = outputFile->Write(buffer, r);
151  else
152  w = 0;
153  } while (Running() && r > 0 && w > 0);
154  DELETENULL(inputFile);
155  DELETENULL(outputFile);
156 
157  // validate result
158  if (!Running() || r < 0 || w < 0) {
159  success = false;
160  break;
161  }
162  }
163  }
164  }
165 
166  // release allocated buffer
167  free(buffer);
168 
169  // delete all created target files and directories
170  if (!success) {
172  RemoveFileOrDir(*target, true);
175  error = "copy failed";
176  return;
177  }
178  }
179  else {
180  // delete all created empty target directories
181  recname = target;
182  recname.Truncate(strlen(*recname) - 1);
183  recname = StripLastDirectory(*recname);
184  do {
185  if (!RemoveEmptyDirectories(*recname, true))
186  break;
187  recname = StripLastDirectory(*recname);
188  }
189  while (strcmp(*recname, VideoDirectory));
190  error = "insufficient free space";
191  return;
192  }
193  }
194 
195  if (deleteSource) {
196  // Recordings' methods require the last directory delimiter to be stripped off
197  source.Truncate(strlen(*source) - 1);
198  cRecording *recording = Recordings.GetByName(*source);
199  if (recording->Delete())
200  Recordings.DelByName(*source, false);
201  }
202  else
204  }
205 }
206 
207 // --- cFileTransfer ----------------------------------------------------------------
208 
212 bool cFileTransfer::error = false;
213 bool cFileTransfer::ended = false;
214 
215 bool cFileTransfer::Start(cRecording *Recording, const char *FileName, bool CopyOnly)
216 {
217  cMutexLock MutexLock(&mutex);
218  if (!copyingThread) {
219  cString NewName = NewVideoFileName(Recording->FileName(), FileName);
220  error = false;
221  ended = false;
222  if (strlen(*NewName)) {
223  copiedVersionName = strdup(*NewName);
224  copyingThread = new cCopyingThread(Recording->FileName(), copiedVersionName, !CopyOnly);
225  return true;
226  }
227  }
228  return false;
229 }
230 
232 {
233  cMutexLock MutexLock(&mutex);
234  bool Interrupted = copyingThread && copyingThread->Active();
235  const char *Error = copyingThread ? copyingThread->Error() : NULL;
237  if (Interrupted || Error) {
238  if (Interrupted)
239  isyslog("file transfer has been interrupted");
240  if (Error)
241  esyslog("ERROR: '%s' during file transfer", Error);
242  RemoveVideoFile(copiedVersionName); //XXX what if this file is currently being replayed?
244  free(copiedVersionName);
245  copiedVersionName = NULL;
246  }
247 }
248 
250 {
251  cMutexLock MutexLock(&mutex);
252  if (copyingThread) {
253  if (copyingThread->Active())
254  return true;
256  Stop();
257  free(copiedVersionName);
258  copiedVersionName = NULL;
259  ended = true;
260  }
261  return false;
262 }
263 
265 {
266  cMutexLock MutexLock(&mutex);
267  bool result = error;
268  error = false;
269  return result;
270 }
271 
273 {
274  cMutexLock MutexLock(&mutex);
275  bool result = ended;
276  ended = false;
277  return result;
278 }