18 #include <glib/gstdio.h> 32 # define g_fopen fopen 42 #include "gnc-xml-backend.hpp" 47 #define XML_URI_PREFIX "xml://" 48 #define FILE_URI_PREFIX "file://" 49 static QofLogModule log_module = GNC_MOD_BACKEND;
51 GncXmlBackend::~GncXmlBackend()
57 GncXmlBackend::check_path (
const char* fullpath,
bool create)
60 char* dirname = g_path_get_dirname (fullpath);
62 auto rc = g_stat (dirname, &statbuf);
65 || (statbuf.st_mode & _S_IFDIR) == 0
67 || !S_ISDIR (statbuf.st_mode)
74 std::string msg {
"Couldn't find directory for "};
76 PWARN (
"Couldn't find directory for %s", fullpath);
82 rc = g_stat (fullpath, &statbuf);
83 if ((rc != 0) && (!create))
87 std::string msg {
"Couldn't find "};
89 PWARN (
"Couldn't find %s", fullpath);
95 && (statbuf.st_mode & _S_IFDIR) != 0
97 && S_ISDIR (statbuf.st_mode)
102 std::string msg {
"Path "};
105 PWARN (
"Path %s is a directory", fullpath);
131 PWARN (
"Might clobber, no force");
139 auto dirname = g_path_get_dirname (
m_fullpath.c_str());
160 GncXmlBackend::session_end()
168 if (!m_linkfile.empty())
169 g_unlink (m_linkfile.c_str());
177 if (!m_lockfile.empty())
183 rv = g_chmod (m_lockfile.c_str(), S_IWRITE | S_IREAD);
185 rv = g_unlink (m_lockfile.c_str());
188 PWARN (
"Error on g_unlink(%s): %d: %s", m_lockfile.c_str(),
189 errno, g_strerror (errno) ? g_strerror (errno) :
"");
199 static QofBookFileType
200 determine_file_type (
const std::string& path)
202 gboolean with_encoding;
203 QofBookFileType v2type;
205 v2type = gnc_is_xml_data_file_v2 (path.c_str(), &with_encoding);
206 if (v2type == GNC_BOOK_XML2_FILE)
210 return GNC_BOOK_XML2_FILE;
214 return GNC_BOOK_XML2_FILE_NO_ENCODING;
217 else if (v2type == GNC_BOOK_POST_XML2_0_0_FILE)
219 return GNC_BOOK_POST_XML2_0_0_FILE;
221 else if (v2type == GNC_BOOK_XML1_FILE)
223 return GNC_BOOK_XML1_FILE;
225 return GNC_BOOK_NOT_OURS;
234 if (loadType != LOAD_TYPE_INITIAL_LOAD)
return;
236 error = ERR_BACKEND_NO_ERR;
238 g_object_unref(m_book);
239 m_book = QOF_BOOK(g_object_ref(book));
244 case GNC_BOOK_XML2_FILE:
245 rc = qof_session_load_from_xml_file_v2 (
this, book,
254 case GNC_BOOK_XML2_FILE_NO_ENCODING:
258 case GNC_BOOK_XML1_FILE:
266 case GNC_BOOK_POST_XML2_0_0_FILE:
268 PWARN (
"Version of Xml file %s is newer than what we can read",
278 PWARN (
"No read permission to file");
282 PWARN (
"Filename is a directory");
286 PWARN (
"File not any known type");
293 if (error != ERR_BACKEND_NO_ERR)
311 if (m_book ==
nullptr)
312 m_book = QOF_BOOK(g_object_ref(book));
313 if (book != m_book)
return;
322 write_to_file (
true);
330 qof_instance_mark_clean(instance);
334 GncXmlBackend::save_may_clobber_data()
339 auto rc = g_stat (
m_fullpath.c_str(), &statbuf);
353 gnc_book_write_accounts_to_xml_filehandle_v2(
this, book, out);
358 GncXmlBackend::write_to_file (
bool make_backup)
377 auto tmp_name = g_new (
char, strlen (
m_fullpath.c_str()) + 12);
379 strcat (tmp_name,
".tmp-XXXXXX");
388 #pragma GCC diagnostic push 389 #pragma GCC diagnostic warning "-Wdeprecated-declarations" 390 if (!mktemp (tmp_name))
391 #pragma GCC diagnostic pop 410 if (gnc_book_write_to_xml_file_v2 (m_book, tmp_name,
411 gnc_prefs_get_file_save_compressed ()))
415 auto rc = g_stat (
m_fullpath.c_str(), &statbuf);
419 g_assert (g_strcmp0 (tmp_name,
"/dev/null") != 0);
422 if (g_chmod (tmp_name, statbuf.st_mode) != 0)
431 PWARN (
"unable to chmod filename %s: %s",
432 tmp_name ? tmp_name :
"(null)",
433 g_strerror (errno) ? g_strerror (errno) :
"");
442 if (chown (tmp_name, -1, statbuf.st_gid) != 0)
448 PWARN (
"unable to chown filename %s: %s",
449 tmp_name ? tmp_name :
"(null)",
450 strerror (errno) ? strerror (errno) :
"");
458 if (g_unlink (
m_fullpath.c_str()) != 0 && errno != ENOENT)
461 PWARN (
"unable to unlink filename %s: %s",
463 g_strerror (errno) ? g_strerror (errno) :
"");
468 if (!link_or_make_backup (tmp_name,
m_fullpath))
471 std::string msg{
"Failed to make backup file "};
477 if (g_unlink (tmp_name) != 0)
480 PWARN (
"unable to unlink temp filename %s: %s",
481 tmp_name ? tmp_name :
"(null)",
482 g_strerror (errno) ? g_strerror (errno) :
"");
492 LEAVE (
" successful save of book=%p to file=%s", m_book,
498 if (g_unlink (tmp_name) != 0)
514 PWARN (
"unable to unlink temp_filename %s: %s",
515 tmp_name ? tmp_name :
"(null)",
516 g_strerror (errno) ? g_strerror (errno) :
"");
523 std::string msg{
"Unable to write to temp file "};
536 copy_file (
const std::string& orig,
const std::string& bkup)
538 constexpr
size_t buf_size = 1024;
541 ssize_t count_write = 0;
542 ssize_t count_read = 0;
549 auto orig_fd = g_open (orig.c_str(), O_RDONLY | flags, 0);
554 auto bkup_fd = g_open (bkup.c_str(),
555 O_WRONLY | O_CREAT | O_TRUNC | O_EXCL | flags, 0600);
564 count_read = read (orig_fd, buf, buf_size);
565 if (count_read == -1 && errno != EINTR)
574 count_write = write (bkup_fd, buf, count_read);
575 if (count_write == -1)
583 while (count_read > 0);
592 GncXmlBackend::link_or_make_backup (
const std::string& orig,
593 const std::string& bkup)
595 gboolean copy_success = FALSE;
598 link (orig.c_str(), bkup.c_str())
606 if (errno == EPERM || errno == ENOSYS
608 || errno == EOPNOTSUPP
619 copy_success = copy_file (orig.c_str(), bkup);
625 PWARN (
"unable to make file backup from %s to %s: %s",
626 orig.c_str(), bkup.c_str(), g_strerror (errno) ? g_strerror (errno) :
"");
637 m_lockfd = g_open (m_lockfile.c_str(), O_RDWR | O_CREAT | O_EXCL ,
646 set_message(
"Unable to create lockfile, make sure that you have write access to the directory.");
651 set_message(
"Unable to create lockfile, data file is on a read-only filesystem.");
655 set_message(
"Unable to create lockfile, no space on filesystem.");
662 PWARN (
"Unable to create the lockfile %s: %s",
663 m_lockfile.c_str(), strerror(errno));
664 set_message(
"Lockfile creation failed. Please see the tracefile for details.");
676 GncXmlBackend::backup_file()
682 auto rc = g_stat (datafile, &statbuf);
684 return (errno == ENOENT);
686 if (determine_file_type (
m_fullpath) == GNC_BOOK_BIN_FILE)
690 auto bkup_ret = link_or_make_backup (
m_fullpath, bin_bkup);
698 auto backup =
m_fullpath +
"." + timestamp + GNC_DATAFILE_EXT;
701 return link_or_make_backup (datafile, backup);
710 GncXmlBackend::remove_old_files ()
712 GStatBuf lockstatbuf, statbuf;
714 if (g_stat (m_lockfile.c_str(), &lockstatbuf) != 0)
717 auto dir = g_dir_open (m_dirname.c_str(), 0, NULL);
723 while ((dent = g_dir_read_name (dir)) != NULL)
728 if (! (g_str_has_suffix (dent,
".LNK") ||
729 g_str_has_suffix (dent,
".xac") ||
730 g_str_has_suffix (dent, GNC_DATAFILE_EXT) ||
731 g_str_has_suffix (dent, GNC_LOGFILE_EXT)))
734 name = g_build_filename (m_dirname.c_str(), dent, (gchar*)NULL);
737 if (!g_str_has_prefix (name,
m_fullpath.c_str()))
744 if (g_strcmp0 (name,
m_fullpath.c_str()) == 0)
751 if (g_str_has_suffix (name,
".LNK"))
754 if ((g_strcmp0 (name, m_linkfile.c_str()) != 0) &&
756 (g_stat (name, &statbuf) == 0) &&
757 (statbuf.st_mtime < lockstatbuf.st_mtime))
759 PINFO (
"remove stale lock file: %s", name);
781 gchar* stamp_start = name +
m_fullpath.size();
782 gchar* expression = g_strdup_printf (
"^\\.[[:digit:]]{14}(\\%s|\\%s|\\.xac)$",
783 GNC_DATAFILE_EXT, GNC_LOGFILE_EXT);
784 gboolean got_date_stamp = FALSE;
786 if (regcomp (&pattern, expression, REG_EXTENDED | REG_ICASE) != 0)
787 PWARN (
"Cannot compile regex for date stamp");
788 else if (regexec (&pattern, stamp_start, 0, NULL, 0) == 0)
789 got_date_stamp = TRUE;
804 if (gnc_prefs_get_file_retention_policy () == XML_RETAIN_NONE)
806 PINFO (
"remove stale file: %s - reason: preference XML_RETAIN_NONE", name);
809 else if ((gnc_prefs_get_file_retention_policy () == XML_RETAIN_DAYS) &&
810 (gnc_prefs_get_file_retention_days () > 0))
815 if (g_stat (name, &statbuf) != 0)
820 days = (int) (difftime (now, statbuf.st_mtime) / 86400);
822 PINFO (
"file retention = %d days", gnc_prefs_get_file_retention_days ());
823 if (days >= gnc_prefs_get_file_retention_days ())
825 PINFO (
"remove stale file: %s - reason: more than %d days old", name, days);
No read access permission for the given file.
couldn't parse the data in the file
#define qof_instance_is_dirty
Return value of is_dirty flag.
char * gnc_date_timestamp(void)
Make a timestamp in YYYYMMDDHHMMSS format.
void set_message(std::string &&)
Set a descriptive message that can be displayed to the user when there's an error.
#define PINFO(format, args...)
Print an informational note.
QofBackendError
The errors that can be reported to the GUI & other front-end users.
void sync(QofBook *book) override
Synchronizes the engine contents to the backend.
gchar * gnc_uri_get_path(const gchar *uri)
Extracts the path part from a uri.
in use by another user (ETXTBSY)
Create a new store at the URI.
couldn't write to the file
Open the session read-only, ignoring any existing lock and not creating one if the URI isn't locked...
#define ENTER(format, args...)
Print a function entry debugging message.
void export_coa(QofBook *) override
Extract the chart of accounts from the current database and create a new database with it...
Create a new store at the URI even if a store already exists there.
void load(QofBook *book, QofBackendLoadType loadType) override
Load the minimal set of application data needed for the application to be operable at initial startup...
api for Version 1 XML-based file format
#define PWARN(format, args...)
Log a warning.
didn't recognize the file type
user login successful, but no permissions to access the desired object
file does not specify encoding
void qof_book_mark_session_saved(QofBook *book)
The qof_book_mark_saved() routine marks the book as having been saved (to a file, to a database)...
api for GnuCash version 2 XML-based file format
file/db version newer than what we can read
void commit(QofInstance *instance) override
Commits the changes from the engine to the backend data storage.
All type declarations for the whole Gnucash engine.
std::string m_fullpath
Each backend resolves a fully-qualified file path.
void session_begin(QofSession *session, const char *new_uri, SessionOpenMode mode) override
Open the file or connect to the server.
Generic api to store and retrieve preferences.
API for the transaction logger.
couldn't make a backup of the file
SessionOpenMode
Mode for opening sessions.
File exists, data would be destroyed.
gboolean qof_book_is_readonly(const QofBook *book)
Return whether the book is read only.
void xaccLogSetBaseName(const char *basepath)
The xaccLogSetBaseName() method sets the base filepath and the root part of the journal file name...
mangled locks (unspecified error)
cannot write to file/directory
gboolean qof_session_load_from_xml_file(QofBook *, const char *filename)
Read in an account group from a file.
#define LEAVE(format, args...)
Print a function exit debugging message.
time64 gnc_time(time64 *tbuf)
get the current time
Utility functions for convert uri in separate components and back.
load and save data to files
void set_error(QofBackendError err)
Set the error value only if there isn't already an error already.
Open will fail if the URI doesn't exist or is locked.