vdr  2.0.6
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 }
struct dirent * Next(void)
Definition: tools.c:1397
cString NewVideoFileName(const char *FileName, const char *NewDirName)
Definition: videodir.c:232
static cMutex mutex
Definition: filetransfer.h:20
ssize_t Write(const void *Data, size_t Size)
Definition: tools.c:1763
bool RemoveVideoFile(const char *FileName)
Definition: videodir.c:171
virtual void Action(void)
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
Definition: filetransfer.c:69
cString source
Definition: filetransfer.c:37
void DelByName(const char *FileName, bool RemoveRecording=true)
Definition: recording.c:1414
ssize_t Read(void *Data, size_t Size)
Definition: tools.c:1705
static bool Error(void)
Definition: filetransfer.c:267
bool MakeDirs(const char *FileName, bool IsDirectory)
Definition: tools.c:396
bool endswith(const char *s, const char *p)
Definition: tools.c:237
static cString sprintf(const char *fmt,...) __attribute__((format(printf
Definition: tools.c:1011
static char * copiedVersionName
Definition: filetransfer.h:21
const char * VideoDirectory
Definition: videodir.c:22
#define esyslog(a...)
Definition: tools.h:34
cString & Truncate(int Index)
Truncate the string at the given Index (if Index is < 0 it is counted from the end of the string)...
Definition: tools.c:1001
void SetPriority(int Priority)
Definition: thread.c:224
int DirSizeMB(const char *DirName)
returns the total size of the files in the given directory, or -1 in case of an error ...
Definition: tools.c:536
cUnbufferedFile is used for large files that are mainly written or read in a streaming manner...
Definition: tools.h:408
#define MALLOC(type, size)
Definition: tools.h:46
cRecording * GetByName(const char *FileName)
Definition: recording.c:1389
const char * Error(void)
Definition: filetransfer.c:44
static bool ended
Definition: filetransfer.h:24
void UpdateByName(const char *FileName)
Definition: recording.c:1436
void bool Start(void)
Actually starts the thread.
Definition: thread.c:273
static bool Ended(void)
Definition: filetransfer.c:275
bool Delete(void)
Changes the file name so that it will no longer be visible in the "Recordings" menu Returns false in ...
Definition: recording.c:1155
static cCopyingThread * copyingThread
Definition: filetransfer.h:22
int FreeDiskSpaceMB(const char *Directory, int *UsedMB)
Definition: tools.c:361
bool Running(void)
Returns false if a derived cThread object shall leave its Action() function.
Definition: thread.h:99
Definition: thread.h:63
void TouchUpdate(void)
Touches the '.update' file in the video directory, so that other instances of VDR that access the sam...
Definition: recording.c:1362
const char * error
Definition: filetransfer.c:35
cString target
Definition: filetransfer.c:38
virtual ~cCopyingThread()
Definition: filetransfer.c:64
void SetIOPriority(int Priority)
Definition: thread.c:230
static bool Start(cRecording *Recording, const char *NewName, bool CopyOnly=false)
Definition: filetransfer.c:218
bool Active(void)
Checks whether the thread is still alive.
Definition: thread.c:298
static bool error
Definition: filetransfer.h:23
cRecordings Recordings
Any access to Recordings that loops through the list of recordings needs to hold a thread lock on thi...
Definition: recording.c:1255
void DELETENULL(T *&p)
Definition: tools.h:48
bool EntriesOnSameFileSystem(const char *File1, const char *File2)
Definition: tools.c:346
#define isyslog(a...)
Definition: tools.h:35
static cString StripLastDirectory(const char *DirName)
Definition: filetransfer.c:13
Definition: thread.h:77
bool RemoveEmptyDirectories(const char *DirName, bool RemoveThis, const char *IgnoreFiles[])
Removes all empty directories under the given directory DirName.
Definition: tools.c:482
static cUnbufferedFile * Create(const char *FileName, int Flags, mode_t Mode=DEFFILEMODE)
Definition: tools.c:1814
bool RemoveFileOrDir(const char *FileName, bool FollowSymlinks)
Definition: tools.c:424
static bool Active(void)
Definition: filetransfer.c:252
const char * FileName(void) const
Definition: recording.c:1002
void Cancel(int WaitSeconds=0)
Cancels the thread by first setting 'running' to false, so that the Action() loop can finish in an or...
Definition: thread.c:323
static void Stop(void)
Definition: filetransfer.c:234
cCopyingThread(const char *SourceName, const char *ToFileName, bool DeleteSource=false)
Definition: filetransfer.c:47
void AddByName(const char *FileName, bool TriggerUpdate=true)
Definition: recording.c:1401
Definition: tools.h:166