Back to index

avfs  1.0.1
serialfile.c
Go to the documentation of this file.
00001 /*
00002     AVFS: A Virtual File System Library
00003     Copyright (C) 1998-2001  Miklos Szeredi <miklos@szeredi.hu>
00004 
00005     This program can be distributed under the terms of the GNU GPL.
00006     See the file COPYING.
00007 */
00008 
00009 #ifdef linux
00010 /* For pread()/pwrite() */
00011 #define _XOPEN_SOURCE 500
00012 #endif
00013 
00014 #include "serialfile.h"
00015 #include "cache.h"
00016 
00017 #include <fcntl.h>
00018 #include <unistd.h>
00019 #include <sys/stat.h>
00020 
00021 struct sfile {
00022     const struct sfilefuncs *func;
00023     void *data;
00024     int flags;
00025     void *conndata;
00026     char *localfile;
00027     avoff_t numbytes;
00028     int fd;
00029     int dirty;
00030     enum { SF_BEGIN, SF_READ, SF_FINI } state;
00031 };
00032 
00033 static void sfile_init(struct sfile *fil)
00034 {
00035     fil->conndata = NULL;
00036     fil->localfile = NULL;
00037     fil->numbytes = 0;
00038     fil->fd = -1;
00039     fil->state = SF_BEGIN;
00040     fil->dirty = 0;
00041 }
00042 
00043 static void sfile_end(struct sfile *fil)
00044 {
00045     close(fil->fd);
00046     av_del_tmpfile(fil->localfile);
00047     av_unref_obj(fil->conndata);
00048 }
00049 
00050 static void sfile_reset(struct sfile *fil)
00051 {
00052     sfile_end(fil);
00053     sfile_init(fil);
00054 }
00055 
00056 static void sfile_reset_usecache(struct sfile *fil)
00057 {
00058     fil->flags &= ~SFILE_NOCACHE;
00059     sfile_reset(fil);
00060 }
00061 
00062 static void sfile_delete(struct sfile *fil)
00063 {
00064     sfile_end(fil);
00065     av_unref_obj(fil->data);
00066 }
00067 
00068 struct sfile *av_sfile_new(const struct sfilefuncs *func,
00069                         void *data, int flags)
00070 {
00071     struct sfile *fil;
00072 
00073     AV_NEW_OBJ(fil, sfile_delete);
00074     fil->func = func;
00075     fil->data = data;
00076     fil->flags = flags;
00077 
00078     sfile_init(fil);
00079 
00080     return fil;
00081 }
00082 
00083 static int sfile_open_localfile(struct sfile *fil)
00084 {
00085     int res;
00086     int openfl;
00087 
00088     res = av_get_tmpfile(&fil->localfile);
00089     if(res < 0)
00090         return res;
00091     
00092     openfl = O_RDWR | O_CREAT | O_TRUNC;
00093     fil->fd = open(fil->localfile, openfl, 0600);
00094     if(fil->fd == -1) {
00095         av_log(AVLOG_ERROR, "Error opening file %s: %s", fil->localfile,
00096                  strerror(errno));
00097         return -EIO;
00098     }
00099     
00100     return 0;
00101 }
00102 
00103 static int sfile_startget(struct sfile *fil)
00104 {
00105     int res;
00106 
00107     if(!(fil->flags & SFILE_NOCACHE)) {
00108         res = sfile_open_localfile(fil);
00109         if(res < 0)
00110             return res;
00111     }
00112     
00113     res = fil->func->startget(fil->data, &fil->conndata);
00114     if(res < 0)
00115         return res;
00116 
00117     fil->state = SF_READ;
00118     
00119     return 0;
00120 }
00121 
00122 
00123 static avssize_t sfile_do_read(struct sfile *fil, char *buf, avssize_t nbyte)
00124 {
00125     avsize_t numbytes;
00126 
00127     numbytes = 0;
00128     while(nbyte > 0) {
00129         avssize_t res;
00130 
00131         res = fil->func->read(fil->conndata, buf, nbyte);
00132         if(res < 0)
00133             return res;
00134         
00135         if(res == 0) {
00136             av_unref_obj(fil->conndata);
00137             fil->conndata = NULL;
00138             fil->state = SF_FINI;
00139             break;
00140         }
00141         
00142         nbyte -= res;
00143         buf += res;
00144         numbytes += res;
00145     }
00146 
00147     return numbytes;
00148 }
00149 
00150 static avssize_t sfile_cached_pwrite(struct sfile *fil, const char *buf,
00151                                      avssize_t nbyte, avoff_t offset)
00152 {
00153     avssize_t res;
00154 
00155     res = pwrite(fil->fd, buf, nbyte, offset);
00156     if(res == -1 && (errno == ENOSPC || errno == EDQUOT)) {
00157         av_cache_diskfull();
00158         res = pwrite(fil->fd, buf, nbyte, offset);
00159     }
00160     if(res == -1) {
00161         av_log(AVLOG_ERROR, "Error writing file %s: %s", fil->localfile,
00162                  strerror(errno));
00163         return -EIO;
00164     }
00165     if(res != nbyte) {
00166         av_log(AVLOG_ERROR, "Error writing file %s: short write",
00167                  fil->localfile);
00168         return -EIO;
00169     }
00170 
00171     /* FIXME: Checking free space is expensive. This should be done in
00172        a more clever way */
00173     if(offset + nbyte > fil->numbytes)
00174         av_cache_checkspace();
00175 
00176     return res;
00177 }
00178 
00179 static avssize_t sfile_read(struct sfile *fil, char *buf, avssize_t nbyte)
00180 {
00181     avssize_t res;
00182 
00183     res = sfile_do_read(fil, buf, nbyte);
00184     if(res <= 0)
00185         return res;
00186 
00187     if(!(fil->flags & SFILE_NOCACHE))
00188         res = sfile_cached_pwrite(fil, buf, res, fil->numbytes);
00189 
00190     if(res > 0)
00191         fil->numbytes += res;
00192 
00193     return res;
00194 }
00195 
00196 static int sfile_dummy_read(struct sfile *fil, avoff_t offset)
00197 {
00198     avssize_t res;
00199     avsize_t nact;
00200     const int tmpbufsize = 8192;
00201     char tmpbuf[tmpbufsize];
00202 
00203     if((fil->flags & SFILE_NOCACHE) != 0)
00204         nact = AV_MIN(tmpbufsize, offset - fil->numbytes);
00205     else
00206         nact = tmpbufsize;
00207 
00208     res = sfile_read(fil, tmpbuf, tmpbufsize);
00209     
00210     if(res < 0)
00211         return res;
00212     
00213     return 0;
00214 }
00215 
00216 static avssize_t sfile_cached_pread(struct sfile *fil, char *buf,
00217                                    avssize_t nbyte, avoff_t offset)
00218 {
00219     avssize_t res;
00220 
00221     if(nbyte == 0)
00222         return 0;
00223 
00224     res = pread(fil->fd, buf, nbyte, offset);
00225     if(res < 0) {
00226         av_log(AVLOG_ERROR, "Error reading file %s: %s", fil->localfile,
00227                  strerror(errno));
00228         return -EIO;
00229     }
00230     if(res != nbyte) {
00231         av_log(AVLOG_ERROR, "Error reading file %s: short read",
00232                  fil->localfile);
00233         return -EIO;
00234     }
00235 
00236     return res;
00237 }
00238 
00239 static avssize_t sfile_finished_read(struct sfile *fil, char *buf,
00240                                      avsize_t nbyte, avoff_t offset)
00241 {
00242     avsize_t nact;
00243     
00244     if(offset >= fil->numbytes)
00245         return 0;
00246         
00247     nact = AV_MIN(nbyte, fil->numbytes - offset);
00248 
00249     return sfile_cached_pread(fil, buf, nact, offset);
00250 }
00251 
00252 static avssize_t sfile_pread(struct sfile *fil, char *buf, avsize_t nbyte,
00253                              avoff_t offset)
00254 {
00255     int res;
00256 
00257     while(fil->state == SF_READ) {
00258         if(offset + nbyte <= fil->numbytes)
00259             return sfile_cached_pread(fil, buf, nbyte, offset);
00260         
00261         if(offset == fil->numbytes)
00262             return sfile_read(fil, buf, nbyte);
00263         
00264         res = sfile_dummy_read(fil, offset);
00265         if(res < 0)
00266             return res;
00267     }
00268 
00269     return sfile_finished_read(fil, buf, nbyte, offset);
00270 }
00271 
00272 static avssize_t sfile_pread_start(struct sfile *fil, char *buf,
00273                                    avsize_t nbyte, avoff_t offset)
00274 {
00275     int res;
00276 
00277     if((fil->flags & SFILE_NOCACHE) != 0 && offset < fil->numbytes)
00278         sfile_reset_usecache(fil);
00279 
00280     if(fil->state == SF_BEGIN) {
00281         res = sfile_startget(fil);
00282         if(res < 0)
00283             return res;
00284     }
00285 
00286     return sfile_pread(fil, buf, nbyte, offset);
00287 }
00288 
00289 static avssize_t sfile_pread_force(struct sfile *fil, char *buf,
00290                                    avsize_t nbyte, avoff_t offset)
00291 {
00292     avssize_t res;
00293 
00294     res = sfile_pread_start(fil, buf, nbyte, offset);
00295     if(res < 0) {
00296         if(res == -EAGAIN && fil->numbytes > 0) {
00297             sfile_reset(fil);
00298             res = sfile_pread_start(fil, buf, nbyte, offset);
00299         }
00300         if(res < 0) {
00301             if(res == -EAGAIN)
00302                 res = -EIO;
00303             
00304             sfile_reset(fil);
00305         }
00306     }
00307 
00308     return res;
00309 }
00310 
00311 avssize_t av_sfile_pread(struct sfile *fil, char *buf, avsize_t nbyte,
00312                            avoff_t offset)
00313 {
00314     if(nbyte == 0)
00315         return 0;
00316     
00317     return sfile_pread_force(fil, buf, nbyte, offset);
00318 }
00319 
00320 
00321 static int sfile_read_until(struct sfile *fil, avoff_t offset, int finish)
00322 {
00323     avssize_t res;
00324 
00325     if(finish && (fil->flags & SFILE_NOCACHE) != 0)
00326         sfile_reset_usecache(fil);
00327     else if(fil->state == SF_FINI)
00328         return 0;
00329 
00330     res = sfile_pread_force(fil, NULL, 0, offset);
00331     if(res < 0)
00332         return res;
00333 
00334     if(finish && fil->state != SF_FINI) {
00335         av_unref_obj(fil->conndata);
00336         fil->conndata = NULL;
00337         fil->state = SF_FINI;
00338     }
00339 
00340     return 0;
00341 }
00342 
00343 avoff_t av_sfile_size(struct sfile *fil)
00344 {
00345     avssize_t res;
00346 
00347     res = sfile_read_until(fil, AV_MAXOFF, 0);
00348     if(res < 0)
00349         return res;
00350 
00351     return fil->numbytes;
00352 }
00353 
00354 int av_sfile_startget(struct sfile *fil)
00355 {
00356     return sfile_read_until(fil, 0, 0);
00357 }
00358 
00359 int av_sfile_truncate(struct sfile *fil, avoff_t length)
00360 {
00361     int res;
00362 
00363     if(length == 0) {
00364         if(fil->state == SF_FINI && fil->numbytes == 0)
00365             return 0;
00366 
00367         sfile_reset_usecache(fil);
00368         res = sfile_open_localfile(fil);
00369         if(res < 0)
00370             return res;
00371 
00372         fil->state = SF_FINI;
00373         fil->dirty = 1;
00374         return 0;
00375     }
00376     
00377     res = sfile_read_until(fil, length, 1);
00378     if(res < 0)
00379         return res;
00380 
00381     if(fil->numbytes > length) {
00382         ftruncate(fil->fd, length);
00383         fil->numbytes = length;
00384         fil->dirty = 1;
00385     }
00386     
00387     return 0;
00388 }
00389 
00390 avssize_t av_sfile_pwrite(struct sfile *fil, const char *buf, avsize_t nbyte,
00391                             avoff_t offset)
00392 {
00393     avssize_t res;
00394     avoff_t end;
00395 
00396     if(nbyte == 0)
00397         return 0;
00398 
00399     res = sfile_read_until(fil, AV_MAXOFF, 1);
00400     if(res < 0)
00401         return res;
00402     
00403     res = sfile_cached_pwrite(fil, buf, nbyte, offset);
00404     if(res < 0) {
00405         sfile_reset(fil);
00406         return res;
00407     }
00408 
00409     end = offset + nbyte; 
00410     if(end > fil->numbytes)
00411         fil->numbytes = end;
00412 
00413     fil->dirty = 1;
00414     return res;
00415 }
00416 
00417 static int sfile_writeout(struct sfile *fil, void *conndata)
00418 {
00419     avssize_t res;
00420     const int tmpbufsize = 8192;
00421     char tmpbuf[tmpbufsize];
00422     avoff_t offset;
00423 
00424     for(offset = 0; offset < fil->numbytes;) {
00425         avsize_t nact = AV_MIN(tmpbufsize, fil->numbytes - offset);
00426 
00427         res = sfile_cached_pread(fil, tmpbuf, nact, offset);
00428         if(res < 0)
00429             return res;
00430         
00431         res = fil->func->write(conndata, tmpbuf, nact);
00432         if(res < 0)
00433             return res;
00434 
00435         offset += res;
00436     }
00437     
00438     return 0;
00439 }
00440 
00441 int av_sfile_flush(struct sfile *fil)
00442 {
00443     int res;
00444     void *conndata;
00445 
00446     if(!fil->dirty)
00447         return 0;
00448     
00449     res = fil->func->startput(fil->data, &conndata);
00450     if(res == 0) {
00451         res = sfile_writeout(fil, conndata);
00452         if(res == 0)
00453             res = fil->func->endput(conndata);
00454     }
00455     av_unref_obj(conndata);
00456     if(res < 0)
00457         sfile_reset(fil);
00458 
00459     fil->dirty = 0;
00460 
00461     return res;
00462 }
00463 
00464 void *av_sfile_getdata(struct sfile *fil)
00465 {
00466     return fil->data;
00467 }
00468 
00469 avoff_t av_sfile_diskusage(struct sfile *fil)
00470 {
00471     int res;
00472     struct stat buf;
00473 
00474     if(fil->fd == -1)
00475         return 0;
00476     
00477     res = fstat(fil->fd, &buf);
00478     if(res == -1) {
00479         av_log(AVLOG_ERROR, "Error in fstat() for %s: %s", fil->localfile,
00480                strerror(errno));
00481         return -EIO;
00482     }
00483     
00484     return buf.st_blocks * 512;
00485 }