ksavefile.cpp
00001 /* 00002 This file is part of the KDE libraries 00003 Copyright (c) 1999 Waldo Bastian <bastian@kde.org> 00004 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Library General Public 00007 License version 2 as published by the Free Software Foundation. 00008 00009 This library is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 Library General Public License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to 00016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00017 Boston, MA 02110-1301, USA. 00018 */ 00019 00020 #include <config.h> 00021 00022 #include <sys/types.h> 00023 00024 #ifdef HAVE_SYS_STAT_H 00025 #include <sys/stat.h> 00026 #endif 00027 00028 #include <unistd.h> 00029 #include <fcntl.h> 00030 00031 #ifdef HAVE_TEST 00032 #include <test.h> 00033 #endif 00034 00035 #include <tqdatetime.h> 00036 #include <tqdir.h> 00037 00038 #include <kde_file.h> 00039 #include "tdeapplication.h" 00040 #include "ksavefile.h" 00041 #include "kstandarddirs.h" 00042 00043 KSaveFile::KSaveFile(const TQString &filename, int mode) 00044 : mTempFile(true) 00045 { 00046 // follow symbolic link, if any 00047 TQString real_filename = TDEStandardDirs::realFilePath(filename); 00048 00049 // we only check here if the directory can be written to 00050 // the actual filename isn't written to, but replaced later 00051 // with the contents of our tempfile 00052 if (!checkAccess(real_filename, W_OK)) 00053 { 00054 mTempFile.setError(EACCES); 00055 return; 00056 } 00057 00058 if (mTempFile.create(real_filename, TQString::fromLatin1(".new"), mode)) 00059 { 00060 mFileName = real_filename; // Set filename upon success 00061 00062 // if we're overwriting an existing file, ensure temp file's 00063 // permissions are the same as existing file so the existing 00064 // file's permissions are preserved 00065 KDE_struct_stat stat_buf; 00066 if (KDE_stat(TQFile::encodeName(real_filename), &stat_buf)==0) 00067 { 00068 // But only if we own the existing file 00069 if (stat_buf.st_uid == getuid()) 00070 { 00071 bool changePermission = true; 00072 if (stat_buf.st_gid != getgid()) 00073 { 00074 if (fchown(mTempFile.handle(), (uid_t) -1, stat_buf.st_gid) != 0) 00075 { 00076 // Use standard permission if we can't set the group 00077 changePermission = false; 00078 } 00079 } 00080 if (changePermission) 00081 fchmod(mTempFile.handle(), stat_buf.st_mode); 00082 } 00083 } 00084 } 00085 } 00086 00087 KSaveFile::~KSaveFile() 00088 { 00089 if (mTempFile.bOpen) 00090 close(); // Close if we were still open 00091 } 00092 00093 TQString 00094 KSaveFile::name() const 00095 { 00096 return mFileName; 00097 } 00098 00099 void 00100 KSaveFile::abort() 00101 { 00102 mTempFile.close(); 00103 mTempFile.unlink(); 00104 } 00105 00106 bool 00107 KSaveFile::close() 00108 { 00109 if (mTempFile.name().isEmpty() || mTempFile.handle()==-1) 00110 return false; // Save was aborted already 00111 if (!mTempFile.sync()) 00112 { 00113 abort(); 00114 return false; 00115 } 00116 if (mTempFile.close()) 00117 { 00118 if (0==KDE_rename(TQFile::encodeName(mTempFile.name()), TQFile::encodeName(mFileName))) 00119 return true; // Success! 00120 mTempFile.setError(errno); 00121 } 00122 // Something went wrong, make sure to delete the interim file. 00123 mTempFile.unlink(); 00124 return false; 00125 } 00126 00127 static int 00128 write_all(int fd, const char *buf, size_t len) 00129 { 00130 while (len > 0) 00131 { 00132 int written = write(fd, buf, len); 00133 if (written < 0) 00134 { 00135 if (errno == EINTR) 00136 continue; 00137 return -1; 00138 } 00139 buf += written; 00140 len -= written; 00141 } 00142 return 0; 00143 } 00144 00145 bool KSaveFile::backupFile( const TQString& qFilename, const TQString& backupDir, 00146 const TQString& backupExtension) 00147 { 00148 TQCString cFilename = TQFile::encodeName(qFilename); 00149 const char *filename = cFilename.data(); 00150 00151 int fd = KDE_open( filename, O_RDONLY ); 00152 if (fd < 0) 00153 return false; 00154 00155 KDE_struct_stat buff; 00156 if ( KDE_fstat( fd, &buff) < 0 ) 00157 { 00158 ::close( fd ); 00159 return false; 00160 } 00161 00162 TQCString cBackup; 00163 if ( backupDir.isEmpty() ) 00164 cBackup = cFilename; 00165 else 00166 { 00167 TQCString nameOnly; 00168 int slash = cFilename.findRev('/'); 00169 if (slash < 0) 00170 nameOnly = cFilename; 00171 else 00172 nameOnly = cFilename.mid(slash + 1); 00173 cBackup = TQFile::encodeName(backupDir); 00174 if ( backupDir[backupDir.length()-1] != (QChar)'/' ) 00175 cBackup += '/'; 00176 cBackup += nameOnly; 00177 } 00178 cBackup += TQFile::encodeName(backupExtension); 00179 const char *backup = cBackup.data(); 00180 int permissions = buff.st_mode & 07777; 00181 00182 if ( KDE_stat( backup, &buff) == 0) 00183 { 00184 if ( unlink( backup ) != 0 ) 00185 { 00186 ::close(fd); 00187 return false; 00188 } 00189 } 00190 00191 mode_t old_umask = umask(0); 00192 int fd2 = KDE_open( backup, O_WRONLY | O_CREAT | O_EXCL, permissions | S_IWUSR); 00193 umask(old_umask); 00194 00195 if ( fd2 < 0 ) 00196 { 00197 ::close(fd); 00198 return false; 00199 } 00200 00201 char buffer[ 32*1024 ]; 00202 00203 while( 1 ) 00204 { 00205 int n = ::read( fd, buffer, 32*1024 ); 00206 if (n == -1) 00207 { 00208 if (errno == EINTR) 00209 continue; 00210 ::close(fd); 00211 ::close(fd2); 00212 return false; 00213 } 00214 if (n == 0) 00215 break; // Finished 00216 00217 if (write_all( fd2, buffer, n)) 00218 { 00219 ::close(fd); 00220 ::close(fd2); 00221 return false; 00222 } 00223 } 00224 00225 ::close( fd ); 00226 00227 if (::close(fd2)) 00228 return false; 00229 return true; 00230 }