Drizzled Public API Documentation

CSFile.cc

00001 /* Copyright (C) 2008 PrimeBase Technologies GmbH, Germany
00002  *
00003  * PrimeBase Media Stream for MySQL
00004  *
00005  * This program is free software; you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation; either version 2 of the License, or
00008  * (at your option) any later version.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program; if not, write to the Free Software
00017  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
00018  *
00019  * Original author: Paul McCullagh (H&G2JCtL)
00020  * Continued development: Barry Leslie
00021  *
00022  * 2007-06-07
00023  *
00024  * CORE SYSTEM:
00025  * Basic file I/O.
00026  *
00027  */
00028 
00029 #include "CSConfig.h"
00030 
00031 #ifdef OS_WINDOWS
00032 #include <sys/utime.h>
00033 #define utimes(f, s) _utime(f, s)
00034 #else
00035 #include <unistd.h>
00036 #include <dirent.h>
00037 #include <sys/time.h>
00038 #endif
00039 #include <stdio.h>
00040 
00041 #include <errno.h>
00042 #include <string.h>
00043 
00044 #include "CSGlobal.h"
00045 #include "CSFile.h"
00046 #include "CSStream.h"
00047 #include "CSMd5.h"
00048 
00049 #define IS_MODE(m, s) ((m & s) == s)
00050 
00051 /*
00052  * ---------------------------------------------------------------
00053  * CORE SYSTEM FILES
00054  */
00055 
00056 CSFile::~CSFile()
00057 {
00058   close();
00059   if (myFilePath)
00060     myFilePath->release();
00061 }
00062 
00063 CSOutputStream *CSFile::getOutputStream()
00064 {
00065   return CSFileOutputStream::newStream(RETAIN(this));
00066 }
00067 
00068 CSOutputStream *CSFile::getOutputStream(off64_t offset)
00069 {
00070   return CSFileOutputStream::newStream(RETAIN(this), offset);
00071 }
00072 
00073 CSInputStream *CSFile::getInputStream()
00074 {
00075   return CSFileInputStream::newStream(RETAIN(this));
00076 }
00077 
00078 CSInputStream *CSFile::getInputStream(off64_t offset)
00079 {
00080   return CSFileInputStream::newStream(RETAIN(this), offset);
00081 }
00082 
00083 bool CSFile::try_CreateAndOpen(CSThread *self, int mode, bool retry)
00084 {
00085   volatile bool rtc = true;
00086   
00087   try_(a) {
00088     openFile(mode);
00089     rtc = false; // success, do not try again.
00090   }
00091   catch_(a) {
00092     if (retry || !isDirNotFound(&self->myException))
00093       throw_();
00094 
00095     /* Make sure the parent directory exists: */
00096     CSPath  *dir = CSPath::newPath(RETAIN(myFilePath), "..");
00097     push_(dir);
00098     try_(b) {
00099       dir->makePath();
00100     }
00101     catch_(b) { /* Two threads may try to create the directory at the same time. */
00102       if (!isDirExists(&self->myException))
00103         throw_();
00104     }
00105     cont_(b);
00106 
00107     release_(dir);
00108   }
00109   cont_(a);
00110   return rtc; // try again.
00111 }
00112 
00113 void CSFile::open(int mode)
00114 {
00115   enter_();
00116   if (mode & CREATE) {
00117     bool retry = false;
00118     while ((retry = try_CreateAndOpen(self, mode, retry)) == true){}    
00119   }
00120   else
00121     openFile(mode);
00122   exit_();
00123 }
00124 
00125 void CSFile::lock()
00126 {
00127   if (!iLocked) {
00128     sf_lock(IS_MODE(iMode, READONLY));
00129   }
00130   iLocked++;
00131 }
00132 
00133 void CSFile::unlock()
00134 {
00135   iLocked--;
00136   if (!iLocked) 
00137     sf_unlock();
00138 }
00139 
00140 bool CSFile::transfer(CSFile *dst_file, off64_t dst_offset, CSFile *src_file, off64_t src_offset, off64_t size, char *buffer, size_t buffer_size)
00141 {
00142   size_t tfer;
00143   enter_();
00144   
00145   push_(dst_file);
00146   push_(src_file);
00147 
00148   while (size > 0) {
00149     if (size > (off64_t) buffer_size)
00150       tfer = buffer_size;
00151     else
00152       tfer = (size_t) size;
00153     if (!(tfer = src_file->read(buffer, src_offset, tfer, 0)))
00154       break;
00155     dst_file->write(buffer, dst_offset, tfer);
00156     dst_offset += tfer;
00157     src_offset += tfer;
00158     size -= tfer;
00159   }
00160   
00161   release_(src_file);
00162   release_(dst_file);
00163   return_(size == 0);
00164 }
00165 
00166 void CSFile::streamOut(CSOutputStream *dst_stream, off64_t src_offset, off64_t size, char *buffer, size_t buffer_size)
00167 {
00168   size_t tfer;
00169   enter_();
00170   
00171   push_(dst_stream);
00172 
00173   while (size > 0) {
00174     if (size > (off64_t) buffer_size)
00175       tfer = buffer_size;
00176     else
00177       tfer = (size_t) size;
00178       
00179     read(buffer, src_offset, tfer, tfer);
00180     dst_stream->write(buffer, tfer);
00181     
00182     src_offset += tfer;
00183     size -= tfer;
00184   }
00185   
00186   release_(dst_stream);
00187   exit_();
00188 }
00189 
00190 #define CS_MASK       ((S_IRUSR | S_IWUSR) | (S_IRGRP | S_IWGRP) | (S_IROTH))
00191 
00192 void CSFile::touch()
00193 {
00194   // don't use futimes() here. On some platforms it will fail if the 
00195   // file was opened readonly.
00196   if (utimes(myFilePath->getCString(), NULL) == -1)
00197     CSException::throwFileError(CS_CONTEXT, myFilePath->getCString(), errno);
00198 }
00199 
00200 void CSFile::close()
00201 {
00202   while (iLocked)
00203     unlock();
00204   sf_close();
00205 }
00206 
00207 off64_t CSFile::getEOF()
00208 {
00209      return sf_getEOF();
00210 }
00211 
00212 void CSFile::setEOF(off64_t offset)
00213 {
00214   sf_setEOF(offset);
00215 }
00216 
00217 size_t CSFile::read(void *data, off64_t offset, size_t size, size_t min_size)
00218 {
00219   size_t read_size;
00220   
00221   enter_();
00222   read_size = sf_pread(data, size, offset);
00223   self->interrupted();
00224   if (read_size < min_size)
00225     CSException::throwEOFError(CS_CONTEXT, myFilePath->getCString());
00226   return_(read_size);
00227 }
00228 
00229 void CSFile::write(const void *data, off64_t offset, size_t size)
00230 {
00231   enter_();
00232     sf_pwrite(data, size, offset);
00233   self->interrupted();
00234   exit_();
00235 }
00236 
00237 void CSFile::flush()
00238 {
00239 }
00240 
00241 void CSFile::sync()
00242 {
00243   sf_sync();
00244 }
00245 
00246 CSFile *CSFile::newFile(CSPath *path)
00247 {
00248   CSFile *f;
00249 
00250   if (!(f = new CSFile())) {
00251     path->release();
00252     CSException::throwOSError(CS_CONTEXT, ENOMEM);
00253   }
00254   f->myFilePath = path;
00255   return f;
00256 }
00257 
00258 CSFile *CSFile::newFile(const char *path_str)
00259 {
00260   CSPath *path;
00261 
00262   path = CSPath::newPath(path_str);
00263   return newFile(path);
00264 }
00265 
00266 CSFile *CSFile::newFile(const char *dir_str, const char *path_str)
00267 {
00268   CSPath *path;
00269 
00270   path = CSPath::newPath(dir_str, path_str);
00271   return newFile(path);
00272 }
00273 
00274 void CSFile::openFile(int mode)
00275 {
00276   if (fs_isOpen() && (iMode != mode))
00277     close();
00278     
00279   if (!fs_isOpen())
00280     sf_open(myFilePath->getCString(), IS_MODE(mode, READONLY), IS_MODE(mode, CREATE));
00281   
00282   iMode = mode;
00283   /* Does not make sense to truncate, and have READONLY! */
00284   if (IS_MODE(mode, TRUNCATE) && !IS_MODE(mode, READONLY))
00285     setEOF((off64_t) 0);
00286 }
00287 
00288 void CSFile::md5Digest(Md5Digest *digest)
00289 {
00290   u_char buffer[1024];
00291   off64_t offset = 0, size;
00292   size_t len;
00293   CSMd5 md5;
00294   enter_();
00295   
00296   size = getEOF();
00297   while (size) {
00298     len = (size_t)((size < 1024)? size:1024);
00299     len = read(buffer, offset, len, len);
00300     offset +=len;
00301     size -= len;
00302     md5.md5_append(buffer, len);
00303   }
00304   md5.md5_get_digest(digest);
00305   exit_();
00306   
00307 }
00308 
00309 /*
00310  * ---------------------------------------------------------------
00311  * A READ BUFFERED FILE
00312  */
00313 
00314 CSReadBufferedFile::CSReadBufferedFile():
00315 myFile(NULL),
00316 iFileBufferOffset(0),
00317 iBufferDataLen(0)
00318 {
00319 }
00320 
00321 CSReadBufferedFile::~CSReadBufferedFile()
00322 {
00323   if (myFile) {
00324     close();
00325     myFile->release();
00326     myFile = NULL;
00327   }
00328 }
00329 
00330 void CSReadBufferedFile::close()
00331 {
00332   flush();
00333   myFile->close();
00334   iFileBufferOffset = 0;
00335   iBufferDataLen = 0;
00336 }
00337 
00338 off64_t CSReadBufferedFile::getEOF()
00339 {
00340   off64_t eof = myFile->getEOF();
00341 
00342   if (eof < iFileBufferOffset + iBufferDataLen)
00343     return iFileBufferOffset + iBufferDataLen;
00344   return eof;
00345 }
00346 
00347 void CSReadBufferedFile::setEOF(off64_t offset)
00348 {
00349   myFile->setEOF(offset);
00350   if (offset < iFileBufferOffset) {
00351     iFileBufferOffset = 0;
00352     iBufferDataLen = 0;
00353   }
00354   else if (offset < iFileBufferOffset + iBufferDataLen)
00355     iBufferDataLen = (size_t)(offset - iFileBufferOffset);
00356 }
00357 
00358 size_t CSReadBufferedFile::read(void *data, off64_t offset, size_t size, size_t min_size)
00359 {
00360   size_t result;
00361   size_t tfer = 0;
00362 
00363   if (iBufferDataLen > 0) {
00364     if (offset < iFileBufferOffset) {
00365       //  case 1, 2, 6
00366       if (offset + size > iFileBufferOffset + iBufferDataLen) {
00367         // 6 (would have to do 2 reads and a memcpy (better is just one read)
00368         flush();
00369         goto readit;
00370       }
00371       if (offset + size > iFileBufferOffset) {
00372         // 2
00373         tfer = (size_t)(offset + size - iFileBufferOffset);
00374         memcpy((char *) data + (iFileBufferOffset - offset), iFileBuffer, tfer);
00375         size -= tfer;
00376       }
00377       // We assume we are reading back to front: 
00378       if (size < SC_DEFAULT_FILE_BUFFER_SIZE) {
00379         size_t mins;
00380 
00381         if (offset + size >= SC_DEFAULT_FILE_BUFFER_SIZE) {
00382           iFileBufferOffset = offset + size - SC_DEFAULT_FILE_BUFFER_SIZE;
00383           mins = SC_DEFAULT_FILE_BUFFER_SIZE;
00384         }
00385         else {
00386           iFileBufferOffset = 0;
00387           mins = (size_t) offset + size;
00388         }
00389         result = myFile->read(iFileBuffer, iFileBufferOffset, SC_DEFAULT_FILE_BUFFER_SIZE, mins);
00390         iBufferDataLen = result;
00391         memcpy(data, iFileBuffer + (offset - iFileBufferOffset), size);
00392       }
00393       else
00394         result = myFile->read(data, offset, size, size);
00395       return size + tfer;
00396     }
00397     if (offset + size <= iFileBufferOffset + iBufferDataLen) {
00398       // 3
00399       memcpy(data, iFileBuffer + (offset - iFileBufferOffset), size);
00400       return size;
00401     }
00402     if (offset < iFileBufferOffset + iBufferDataLen) {
00403       // 4 We assume we are reading front to back
00404       tfer = (size_t)(iFileBufferOffset + iBufferDataLen - offset);
00405       memcpy(data, iFileBuffer + (offset - iFileBufferOffset), tfer);
00406       data = (char *) data + tfer;
00407       size -= tfer;
00408       offset += tfer;
00409       if (min_size >= tfer)
00410         min_size -= tfer;
00411       else
00412         min_size = 0;
00413     }
00414     // else 5
00415   }
00416 
00417   readit:
00418   if (size < SC_DEFAULT_FILE_BUFFER_SIZE) {
00419     result = myFile->read(iFileBuffer, offset, SC_DEFAULT_FILE_BUFFER_SIZE, min_size);
00420     iFileBufferOffset = offset;
00421     iBufferDataLen = result;
00422     if (result > size)
00423       result = size;
00424     memcpy(data, iFileBuffer, result);
00425   }
00426   else
00427     result = myFile->read(data, offset, size, min_size);
00428   return result + tfer;
00429 }
00430 
00431 void CSReadBufferedFile::write(const void *data, off64_t offset, size_t size)
00432 {
00433   if (iBufferDataLen > 0) {
00434     size_t tfer;
00435 
00436     if (offset < iFileBufferOffset) {
00437       //  case 1, 2, 6
00438       if (offset + size > iFileBufferOffset + iBufferDataLen) {
00439         // 6 (would have to do 2 reads and a memcpy (better is just one read)
00440         memcpy((char *) data + (iFileBufferOffset - offset), iFileBuffer, iBufferDataLen);
00441       }
00442       else if (offset + size > iFileBufferOffset) {
00443         // 2
00444         tfer = (size_t)(offset + size - iFileBufferOffset);
00445         memcpy(iFileBuffer, (char *) data + (iFileBufferOffset - offset), tfer);
00446       }
00447     }
00448     else if (offset + size <= iFileBufferOffset + iBufferDataLen) {
00449       // 3
00450       memcpy(iFileBuffer + (offset - iFileBufferOffset), data, size);
00451     }
00452     else if (offset < iFileBufferOffset + iBufferDataLen) {
00453       // 4 We assume we are reading front to back
00454       tfer = (size_t)(iFileBufferOffset + iBufferDataLen - offset);
00455       memcpy(iFileBuffer + (offset - iFileBufferOffset), data, tfer);
00456     }
00457     // else 5
00458   }
00459 
00460   myFile->write(data, offset, size);
00461 }
00462 
00463 void CSReadBufferedFile::flush()
00464 {
00465   myFile->flush();
00466 }
00467 
00468 void CSReadBufferedFile::sync()
00469 {
00470   flush();
00471   myFile->sync();
00472 }
00473 
00474 const char *CSReadBufferedFile::getEOL()
00475 {
00476   return myFile->getEOL();
00477 }
00478 
00479 void CSReadBufferedFile::openFile(int mode)
00480 {
00481   myFile->openFile(mode);
00482 }
00483 
00484 
00485 /*
00486  * ---------------------------------------------------------------
00487  * FILE BASED ON THE STANDARD C FILE
00488  */