Main Page | Namespace List | Class Hierarchy | Class List | Directories | File List | Namespace Members | Class Members | File Members

AtomicFile.C

Go to the documentation of this file.
00001 // AtomicFile.C
00002 
00003 // Copyright (C) 2001, Compaq Computer Corporation
00004 
00005 // This file is part of Vesta.
00006 
00007 // Vesta is free software; you can redistribute it and/or
00008 // modify it under the terms of the GNU Lesser General Public
00009 // License as published by the Free Software Foundation; either
00010 // version 2.1 of the License, or (at your option) any later version.
00011 
00012 // Vesta is distributed in the hope that it will be useful,
00013 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015 // Lesser General Public License for more details.
00016 
00017 // You should have received a copy of the GNU Lesser General Public
00018 // License along with Vesta; if not, write to the Free Software
00019 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00020 
00021 //
00022 // Atomically create or replace files.
00023 //
00024 
00025 #include <sys/types.h>
00026 #include <sys/stat.h>
00027 #include <stdio.h>
00028 #include <unistd.h>
00029 #include <fcntl.h>
00030 
00031 #include <errno.h>
00032 #include <Basics.H>
00033 #include "AtomicFile.H"
00034 #include "FS.H"
00035 
00036 class AFStaticStuff {
00037   public:
00038     char *suffix;
00039     int suffixlen; //strlen(suffix);
00040     AFStaticStuff();
00041 };
00042 
00043 static AFStaticStuff afss;
00044 
00045 const char AtomicFile::reserved_char = ';';
00046 
00047 AFStaticStuff::AFStaticStuff()
00048 {
00049     time_t now = time(0);
00050     const int bufsize = 64;
00051     // I sure would like a real text package!
00052     suffix = NEW_PTRFREE_ARRAY(char, bufsize);
00053     sprintf(suffix, "%c%x", AtomicFile::reserved_char, now);
00054     suffixlen = strlen(suffix);
00055     assert(suffixlen < bufsize);
00056 }
00057 
00058 void AtomicFile::open(const char* name, std::ios::openmode mode, int prot) 
00059   throw ()
00060 {
00061     if (!(mode & (std::ios::out|std::ios::app))) {
00062         clear(std::ios::badbit); // set bad bit
00063         errno = EINVAL;
00064         return;
00065     }
00066     realname = NEW_PTRFREE_ARRAY(char, strlen(name)+1);
00067     strcpy(realname, name);
00068     tempname = NEW_PTRFREE_ARRAY(char, strlen(name) + afss.suffixlen + 1);
00069 
00070     strcpy(tempname, name);
00071     strcat(tempname, afss.suffix);
00072 
00073     // ISO C++ removed std::ios::noreplace.  The gcc 3 FAQ makes the
00074     // following stupid suggestion:
00075 
00076     //   To be safe, you can open the file for reading, check if it
00077     //   has been opened, and then decide whether you want to
00078     //   create/replace or not.
00079 
00080     // This would create a race condition between two threads in the
00081     // same program trying to open the same file for an atomic write
00082     // (which is clearly the point of using std::ios::noreplace).  So
00083     // we do it the hard way: use open(2) with O_CREAT|O_EXCL, and
00084     // only open the stream if that succeeds.
00085 
00086     int fd;
00087     do
00088       fd = ::open(tempname, O_WRONLY|O_CREAT|O_EXCL, prot);
00089     while((fd == -1) && (errno == EINTR));
00090 
00091     if(fd != -1)
00092       {
00093         FS::OFdStream::open(tempname, mode);
00094         int close_res;
00095         do
00096           close_res = ::close(fd);
00097         while((close_res != 0) && (errno == EINTR));
00098       }
00099     else
00100       {
00101         clear(std::ios::badbit); // set bad bit
00102       }
00103 }
00104 
00105 static void CheckStream(const std::ostream *ofs, const char *msg) {
00106     if (ofs->good() == 0) {
00107       std::cerr << "AtomicFile::close error: " << msg << std::endl;
00108       abort();
00109     }
00110 }
00111 
00112 void AtomicFile::close() throw ()
00113 {
00114     if (tempname != NULL) {
00115         /* It is very important to check that this stream is good
00116            both before and after closing it because even if the
00117            stream is bad on entry, closing it can make it good again! */
00118         CheckStream(this, "stream bad on entry");
00119         // Also, make sure that any pending writes get completed now.
00120         // If there are pending writes when we call ofstream::close
00121         // and they fail (i.e. due to the disk filling up), the
00122         // stream's state may not indicate failure.
00123         flush();
00124         CheckStream(this, "stream bad after flush");
00125         // Make sure we commit all buffered writes to disk before
00126         // closing and renaming.  (If we don't do this, the system
00127         // could crash after the rename but before the OS flushes all
00128         // writes to disk.)
00129         {
00130           int fsync_res;
00131           do
00132             fsync_res = fsync(this->fd());
00133           while((fsync_res != 0) && (errno == EINTR));
00134           if(fsync_res != 0)
00135             {
00136               int errno_save = errno;
00137               std::cerr << "AtomicFile::close error on fsync(2): "
00138                         << Basics::errno_Text(errno_save) << std::endl;
00139               abort();
00140             }
00141         }
00142         FS::OFdStream::close(); // invoke parent method
00143         CheckStream(this, "close() failed");
00144         if (rename(tempname, realname) != 0) {
00145           int errno_save = errno;
00146           std::cerr << "AtomicFile::close error: rename(2) failed!"
00147                     << std::endl
00148                     << "  error : " << Basics::errno_Text(errno_save)
00149                     << " (errno = " << errno_save << ")" << std::endl
00150                     << "  old = " << tempname << std::endl
00151                     << "  new = " << realname << std::endl;
00152           abort();
00153         }
00154         delete[] tempname;
00155         delete[] realname;
00156         tempname = realname = NULL;
00157     }
00158 }
00159 
00160 AtomicFile::~AtomicFile() throw ()
00161 {
00162     if (tempname != NULL) {
00163       FS::OFdStream::close();
00164         delete[] tempname;
00165         delete[] realname;
00166         tempname = realname = NULL;
00167     }
00168 }
00169 
00170 #include <sys/types.h>
00171 #include <dirent.h>
00172 #if defined(__digital__)
00173 // add declaration to fix broken <dirent.h> header file
00174 extern "C" int _Preaddir_r(DIR *, struct dirent *, struct dirent **);
00175 #endif
00176 
00177 void AtomicFile::cleanup(const char* dirname) throw ()
00178 {
00179     int namebuflen = strlen(dirname) + 258;
00180     char *namebuf = NEW_PTRFREE_ARRAY(char, namebuflen);
00181     DIR *dir = opendir(dirname);
00182     if (!dir) return;
00183     struct dirent de, *done;
00184     while (readdir_r(dir, /*OUT*/ &de, /*OUT*/ &done) == 0 && done != NULL) {
00185         char *suffixpos = strchr(de.d_name, AtomicFile::reserved_char);
00186         if (!suffixpos) continue;
00187         if (strcmp(suffixpos, afss.suffix) != 0) {
00188           sprintf(namebuf, "%s%c%s", dirname, PathnameSep, de.d_name);
00189             // std::cout << "unlinking " << namebuf << std::endl;
00190             remove(namebuf);
00191         }
00192     }
00193 }
00194 
00195 void FS::Close(AtomicFile &vaf) throw (FS::Failure)
00196 {
00197   // Since AtomicFile::close() is careful about checking the state of
00198   // the stream and does its own error handling, we don't need to.
00199   vaf.close();
00200 }
00201 

Generated on Mon May 8 00:48:30 2006 for Vesta by  doxygen 1.4.2