vdr  2.0.4
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  target.Truncate(strlen(*target) - 1);
203  }
204 
205  // Must inform all VDR instances about the modification
207  }
208 }
209 
210 // --- cFileTransfer ----------------------------------------------------------------
211 
215 bool cFileTransfer::error = false;
216 bool cFileTransfer::ended = false;
217 
218 bool cFileTransfer::Start(cRecording *Recording, const char *FileName, bool CopyOnly)
219 {
220  cMutexLock MutexLock(&mutex);
221  if (!copyingThread) {
222  cString NewName = NewVideoFileName(Recording->FileName(), FileName);
223  error = false;
224  ended = false;
225  if (strlen(*NewName)) {
226  copiedVersionName = strdup(*NewName);
227  copyingThread = new cCopyingThread(Recording->FileName(), copiedVersionName, !CopyOnly);
228  return true;
229  }
230  }
231  return false;
232 }
233 
235 {
236  cMutexLock MutexLock(&mutex);
237  bool Interrupted = copyingThread && copyingThread->Active();
238  const char *Error = copyingThread ? copyingThread->Error() : NULL;
240  if (Interrupted || Error) {
241  if (Interrupted)
242  isyslog("file transfer has been interrupted");
243  if (Error)
244  esyslog("ERROR: '%s' during file transfer", Error);
245  RemoveVideoFile(copiedVersionName); //XXX what if this file is currently being replayed?
247  free(copiedVersionName);
248  copiedVersionName = NULL;
249  }
250 }
251 
253 {
254  cMutexLock MutexLock(&mutex);
255  if (copyingThread) {
256  if (copyingThread->Active())
257  return true;
259  Stop();
260  free(copiedVersionName);
261  copiedVersionName = NULL;
262  ended = true;
263  }
264  return false;
265 }
266 
268 {
269  cMutexLock MutexLock(&mutex);
270  bool result = error;
271  error = false;
272  return result;
273 }
274 
276 {
277  cMutexLock MutexLock(&mutex);
278  bool result = ended;
279  ended = false;
280  return result;
281 }
282