ksavefile.cpp
/*
This file is part of the KDE libraries
Copyright (c) 1999 Waldo Bastian <bastian@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include <config.h>
#include <sys/types.h>
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#include <unistd.h>
#include <fcntl.h>
#ifdef HAVE_TEST
#include <test.h>
#endif
#include <qdatetime.h>
#include <qdir.h>
#include <kde_file.h>
#include "kapplication.h"
#include "ksavefile.h"
#include "kstandarddirs.h"
KSaveFile::KSaveFile(const QString &filename, int mode)
: mTempFile(true)
{
// follow symbolic link, if any
QString real_filename = KStandardDirs::realFilePath(filename);
// we only check here if the directory can be written to
// the actual filename isn't written to, but replaced later
// with the contents of our tempfile
if (!checkAccess(real_filename, W_OK))
{
mTempFile.setError(EACCES);
return;
}
if (mTempFile.create(real_filename, QString::fromLatin1(".new"), mode))
{
mFileName = real_filename; // Set filename upon success
// if we're overwriting an existing file, ensure temp file's
// permissions are the same as existing file so the existing
// file's permissions are preserved
KDE_struct_stat stat_buf;
if (KDE_stat(QFile::encodeName(real_filename), &stat_buf)==0)
{
// But only if we own the existing file
if (stat_buf.st_uid == getuid())
{
bool changePermission = true;
if (stat_buf.st_gid != getgid())
{
if (fchown(mTempFile.handle(), (uid_t) -1, stat_buf.st_gid) != 0)
{
// Use standard permission if we can't set the group
changePermission = false;
}
}
if (changePermission)
fchmod(mTempFile.handle(), stat_buf.st_mode);
}
}
}
}
KSaveFile::~KSaveFile()
{
if (mTempFile.bOpen)
close(); // Close if we were still open
}
QString
KSaveFile::name() const
{
return mFileName;
}
void
KSaveFile::abort()
{
mTempFile.close();
mTempFile.unlink();
}
bool
KSaveFile::close()
{
if (mTempFile.name().isEmpty() || mTempFile.handle()==-1)
return false; // Save was aborted already
if (!mTempFile.sync())
{
abort();
return false;
}
if (mTempFile.close())
{
if (0==KDE_rename(QFile::encodeName(mTempFile.name()), QFile::encodeName(mFileName)))
return true; // Success!
mTempFile.setError(errno);
}
// Something went wrong, make sure to delete the interim file.
mTempFile.unlink();
return false;
}
static int
write_all(int fd, const char *buf, size_t len)
{
while (len > 0)
{
int written = write(fd, buf, len);
if (written < 0)
{
if (errno == EINTR)
continue;
return -1;
}
buf += written;
len -= written;
}
return 0;
}
bool KSaveFile::backupFile( const QString& qFilename, const QString& backupDir,
const QString& backupExtension)
{
QCString cFilename = QFile::encodeName(qFilename);
const char *filename = cFilename.data();
int fd = KDE_open( filename, O_RDONLY );
if (fd < 0)
return false;
KDE_struct_stat buff;
if ( KDE_fstat( fd, &buff) < 0 )
{
::close( fd );
return false;
}
QCString cBackup;
if ( backupDir.isEmpty() )
cBackup = cFilename;
else
{
QCString nameOnly;
int slash = cFilename.findRev('/');
if (slash < 0)
nameOnly = cFilename;
else
nameOnly = cFilename.mid(slash + 1);
cBackup = QFile::encodeName(backupDir);
if ( backupDir[backupDir.length()-1] != '/' )
cBackup += '/';
cBackup += nameOnly;
}
cBackup += QFile::encodeName(backupExtension);
const char *backup = cBackup.data();
int permissions = buff.st_mode & 07777;
if ( KDE_stat( backup, &buff) == 0)
{
if ( unlink( backup ) != 0 )
{
::close(fd);
return false;
}
}
mode_t old_umask = umask(0);
int fd2 = KDE_open( backup, O_WRONLY | O_CREAT | O_EXCL, permissions | S_IWUSR);
umask(old_umask);
if ( fd2 < 0 )
{
::close(fd);
return false;
}
char buffer[ 32*1024 ];
while( 1 )
{
int n = ::read( fd, buffer, 32*1024 );
if (n == -1)
{
if (errno == EINTR)
continue;
::close(fd);
::close(fd2);
return false;
}
if (n == 0)
break; // Finished
if (write_all( fd2, buffer, n))
{
::close(fd);
::close(fd2);
return false;
}
}
::close( fd );
if (::close(fd2))
return false;
return true;
}
KSaveFile::~KSaveFile
~KSaveFile()
The destructor closes the file.
Definition: ksavefile.cpp:87
KStandardDirs::realFilePath
static QString realFilePath(const QString &filename)
Expands all symbolic links and resolves references to '/.
Definition: kstandarddirs.cpp:704
KSaveFile::abort
void abort()
Aborts the write operation and removes any intermediate files This implies a close.
Definition: ksavefile.cpp:100
KSaveFile::close
bool close()
Closes the file and makes the changes definitive.
Definition: ksavefile.cpp:107
KTempFile::sync
bool sync()
Flushes file to disk (fsync).
Definition: ktempfile.cpp:216
KSaveFile::name
QString name() const
The name of the file as passed to the constructor.
Definition: ksavefile.cpp:94
KTempFile::handle
int handle() const
An integer file descriptor open for writing to the file.
Definition: ktempfile.cpp:149
KSaveFile::KSaveFile
KSaveFile(const QString &filename, int mode=0666)
Creates a new KSaveFile with the given file name.
Definition: ksavefile.cpp:43
KSaveFile::backupFile
static bool backupFile(const QString &filename, const QString &backupDir=QString::null, const QString &backupExtension=QString::fromLatin1("~"))
Static method to create a backup file before saving.
Definition: ksavefile.cpp:145
KTempFile::unlink
void unlink()
Unlinks the file from the directory.
Definition: ktempfile.cpp:202
KTempFile::close
bool close()
Closes the file.
Definition: ktempfile.cpp:253
KTempFile::name
QString name() const
Returns the full path and name of the file.
Definition: ktempfile.cpp:143

KDE Home | KDE Accessibility Home | Description of Access Keys