vdr  1.7.27
filetransfer.c
Go to the documentation of this file.
00001 /*
00002  * filetransfer.c: The video file transfer facilities
00003  *
00004  * See the main source file 'vdr.c' for copyright information and
00005  * how to reach the author.
00006  *
00007  * $Id: $
00008  */
00009 
00010 #include "videodir.h"
00011 #include "filetransfer.h"
00012 
00013 static cString StripLastDirectory(const char *DirName)
00014 {
00015   if (DirName && *DirName) {
00016      cString s(DirName);
00017      int l = strlen(*s);
00018      const char *p = *s + l;
00019      while (l > 0) {
00020            if (*p-- == '/')
00021               break;
00022            l--;
00023            }
00024      if (l)
00025         s = s.Truncate(l);
00026      return s;
00027      }
00028   return NULL;
00029 }
00030 
00031 // --- cCopyingThread --------------------------------------------------------
00032 
00033 class cCopyingThread : public cThread {
00034 private:
00035   const char *error;
00036   bool deleteSource;
00037   cString source;
00038   cString target;
00039 protected:
00040   virtual void Action(void);
00041 public:
00042   cCopyingThread(const char *SourceName, const char *ToFileName, bool DeleteSource = false);
00043   virtual ~cCopyingThread();
00044   const char *Error(void) { return error; }
00045   };
00046 
00047 cCopyingThread::cCopyingThread(const char *SourceName, const char *TargetName, bool DeleteSource)
00048 :cThread("copying"),
00049  error(NULL),
00050  deleteSource(DeleteSource),
00051  source(SourceName),
00052  target(TargetName)
00053 {
00054   // add missing directory delimiters
00055   const char *delim = "/";
00056   if (!endswith(*source, delim))
00057      source = cString::sprintf("%s%s", *source, delim);
00058   if (!endswith(*target, delim))
00059      target = cString::sprintf("%s%s", *target, delim);
00060 
00061   Start();
00062 }
00063 
00064 cCopyingThread::~cCopyingThread()
00065 {
00066   Cancel(3);
00067 }
00068 
00069 void cCopyingThread::Action(void)
00070 {
00071   SetPriority(19);
00072   SetIOPriority(7);
00073 
00074   if (strcmp(*source, *target)) {
00075      // validate target directory
00076      if (strstr(*target, *source)) {
00077         error = "invalid target";
00078         return;
00079         }
00080 
00081      // recordings methods require the last directory delimiter to be stripped off
00082      cString recname = target;
00083      recname.Truncate(strlen(*recname) - 1);
00084      Recordings.AddByName(*recname, false);
00085 
00086      RemoveFileOrDir(*target);
00087      if (!MakeDirs(*target, true)) {
00088         error = "MakeDirs";
00089         return;
00090         }
00091 
00092      if (deleteSource && EntriesOnSameFileSystem(*source, *target)) {
00093         if (rename(*source, *target) == -1) {
00094            error = "rename";
00095            return;
00096            }
00097         // delete all empty source directories
00098         recname = source;
00099         recname.Truncate(strlen(*recname) - 1);
00100         recname = StripLastDirectory(*recname);
00101         do {
00102            if (!RemoveEmptyDirectories(*recname, true))
00103               break;
00104            recname = StripLastDirectory(*recname);
00105            }
00106         while (strcmp(*recname, VideoDirectory));
00107         }
00108      else {
00109         int required = DirSizeMB(*source);
00110         int available = FreeDiskSpaceMB(*target);
00111 
00112         // validate free space
00113         if (required < available) {
00114            cReadDir d(*source);
00115            struct dirent *e;
00116            bool success = true;
00117 
00118            // allocate copying buffer
00119            const int len = 1024 * 1024;
00120            char *buffer = MALLOC(char, len);
00121            if (!buffer) {
00122               error = "MALLOC";
00123               return;
00124               }
00125 
00126            // loop through all files, but skip all sub-directories
00127            while (Running() && (e = d.Next()) != NULL) {
00128                  // skip generic entries
00129                  if (strcmp(e->d_name, ".") && strcmp(e->d_name, "..") && strcmp(e->d_name, "lost+found")) {
00130                     cString sourceFile = cString::sprintf("%s%s", *source, e->d_name);
00131                     cString targetFile = cString::sprintf("%s%s", *target, e->d_name);
00132 
00133                     // copy only regular files
00134                     struct stat sts;
00135                     if (!stat(*sourceFile, &sts) && S_ISREG(sts.st_mode)) {
00136                        int r = -1, w = -1;
00137                        cUnbufferedFile *inputFile = cUnbufferedFile::Create(*sourceFile, O_RDONLY | O_LARGEFILE);
00138                        cUnbufferedFile *outputFile = cUnbufferedFile::Create(*targetFile, O_RDWR | O_CREAT | O_LARGEFILE);
00139 
00140                        // validate files
00141                        if (!inputFile || !outputFile) {
00142                           success = false;
00143                           break;
00144                           }
00145 
00146                        // do actual copy
00147                        do {
00148                          r = inputFile->Read(buffer, len);
00149                          if (r > 0)
00150                             w = outputFile->Write(buffer, r);
00151                          else
00152                             w = 0;
00153                        } while (Running() && r > 0 && w > 0);
00154                        DELETENULL(inputFile);
00155                        DELETENULL(outputFile);
00156 
00157                        // validate result
00158                        if (!Running() || r < 0 || w < 0) {
00159                           success = false;
00160                           break;
00161                           }
00162                        }
00163                    }
00164                 }
00165 
00166            // release allocated buffer
00167            free(buffer);
00168 
00169            // delete all created target files and directories
00170            if (!success) {
00171               target = StripLastDirectory(*target);
00172               RemoveFileOrDir(*target, true);
00173               target = StripLastDirectory(*target);
00174               RemoveEmptyDirectories(*target, true);
00175               error = "copy failed";
00176               return;
00177               }
00178            }
00179         else {
00180            // delete all created empty target directories
00181            recname = target;
00182            recname.Truncate(strlen(*recname) - 1);
00183            recname = StripLastDirectory(*recname);
00184            do {
00185               if (!RemoveEmptyDirectories(*recname, true))
00186                  break;
00187               recname = StripLastDirectory(*recname);
00188               }
00189            while (strcmp(*recname, VideoDirectory));
00190            error = "insufficient free space";
00191            return;
00192            }
00193         }
00194 
00195      if (deleteSource) {
00196         // Recordings' methods require the last directory delimiter to be stripped off
00197         source.Truncate(strlen(*source) - 1);
00198         cRecording *recording = Recordings.GetByName(*source);
00199         if (recording->Delete())
00200            Recordings.DelByName(*source, false);
00201         }
00202      else
00203         Recordings.TouchUpdate();
00204      }
00205 }
00206 
00207 // --- cFileTransfer ----------------------------------------------------------------
00208 
00209 cMutex cFileTransfer::mutex;
00210 char *cFileTransfer::copiedVersionName = NULL;
00211 cCopyingThread *cFileTransfer::copyingThread = NULL;
00212 bool cFileTransfer::error = false;
00213 bool cFileTransfer::ended = false;
00214 
00215 bool cFileTransfer::Start(cRecording *Recording, const char *FileName, bool CopyOnly)
00216 {
00217   cMutexLock MutexLock(&mutex);
00218   if (!copyingThread) {
00219      cString NewName = NewVideoFileName(Recording->FileName(), FileName);
00220      error = false;
00221      ended = false;
00222      if (strlen(*NewName)) {
00223         copiedVersionName = strdup(*NewName);
00224         copyingThread = new cCopyingThread(Recording->FileName(), copiedVersionName, !CopyOnly);
00225         return true;
00226         }
00227      }
00228   return false;
00229 }
00230 
00231 void cFileTransfer::Stop(void)
00232 {
00233   cMutexLock MutexLock(&mutex);
00234   bool Interrupted = copyingThread && copyingThread->Active();
00235   const char *Error = copyingThread ? copyingThread->Error() : NULL;
00236   DELETENULL(copyingThread);
00237   if ((Interrupted || Error)) {
00238      if (Interrupted)
00239         isyslog("file transfer has been interrupted");
00240      if (Error)
00241         esyslog("ERROR: '%s' during file transfer", Error);
00242      RemoveVideoFile(copiedVersionName); //XXX what if this file is currently being replayed?
00243      Recordings.DelByName(copiedVersionName);
00244      free(copiedVersionName);
00245      copiedVersionName = NULL;
00246      }
00247 }
00248 
00249 bool cFileTransfer::Active(void)
00250 {
00251   cMutexLock MutexLock(&mutex);
00252   if (copyingThread) {
00253      if (copyingThread->Active())
00254         return true;
00255      error = copyingThread->Error();
00256      Stop();
00257      free(copiedVersionName);
00258      copiedVersionName = NULL;
00259      ended = true;
00260      }
00261   return false;
00262 }
00263 
00264 bool cFileTransfer::Error(void)
00265 {
00266   cMutexLock MutexLock(&mutex);
00267   bool result = error;
00268   error = false;
00269   return result;
00270 }
00271 
00272 bool cFileTransfer::Ended(void)
00273 {
00274   cMutexLock MutexLock(&mutex);
00275   bool result = ended;
00276   ended = false;
00277   return result;
00278 }